# packages

pkgId: 216a465cfd19ca448a3a1ee050957bacce181b9a34077fb85c8a50feccba832b
name: python-cryptlex.lexfloatclient
arch: src
version: 4.9.2
epoch: 0
release: 1
summary: LexFloatClient API wrapper for Python
url: https://github.com/cryptlex/lexfloatclient-python
rpm_license: None
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptlex.lexfloatclient-4.9.2-1.src.rpm
checksum_type: sha256
description:
  ![Publish cryptlex.lexfloatclient package](https://github.com/cryptlex/lexfloatclient-python/workflows/Publish%20cryptlex.lexfloatclient%20package/badge.svg)



  Cryptlex lets you license your software apps effortlessly. You can easily generate license keys using the REST API or Dashboard and validate the license keys in your software apps using LexActivator (Cryptlex client library), implement floating licenses using LexFloatClient library.

  Additionally, you can offer trials, subscriptions, floating licenses and much more.

  **cryptlex.lexfloatclient** is a Python wrapper for LexFloatClient licensing library.



      pip install cryptlex.lexfloatclient

  Then you can include it in your code:

  	from cryptlex.lexfloatclient import LexFloatClient, LexFloatStatusCodes, LexFloatClientException



  Refer to following for documentation:

  https://docs.cryptlex.com/floating-licenses/on-premise-floating-licenses/using-lexfloatclient/using-lexfloatclient-with-python


pkgId: 600c359fefa19a802c0f833ba9a313a8251dd37987cfd7c7af70d69bb6c4b913
name: python-crypto
arch: src
version: 1.4.1
epoch: 0
release: 1
summary: Simple symmetric GPG file encryption and decryption
url: https://github.com/chrissimpkins/crypto
rpm_license: MIT license
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-1.4.1-1.src.rpm
checksum_type: sha256
description:
  crypto provides a simple interface to symmetric Gnu Privacy Guard (gpg) encryption and decryption for one or more files on Unix and Linux platforms.  It runs on top of gpg and requires a gpg install on your system.  Encryption is performed with the AES256 cipher algorithm. `Benchmarks relative to default gpg settings are available for text and binary file mime types <https://chrissimpkins.github.io/crypto/benchmarks.html>`_.
  crypto provides a number of options including automated tar archives of multiple files prior to encryption, portable ASCII armored encryption formatting, and SHA256 hash digest generation for your encrypted files.  You can view all available options in the `usage documentation <http://chrissimpkins.github.io/crypto/usage.html>`_ or with the ``--help`` option.
  Tested in cPython 2.7.x, 3.4.x, and pypy 2.6.x (Python version 2.7.9)


pkgId: c18ecc3d0528ced4a2701b19fdbf2a810b76a5df588c0b722e9394f0829f3113
name: python-crypto-condor
arch: src
version: 2024.9.24
epoch: 0
release: 1
summary: Compliance testing for implementations of cryptographic primitives
url: https://github.com/quarkslab/crypto-condor
rpm_license: Apache-2.0
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-condor-2024.9.24-1.src.rpm
checksum_type: sha256
description:
  <img align="right" src="crypto-condor.png" width="250" alt="The logo of crypto-condor, depicting a condor holding a key.">

  crypto-condor is a tool for compliance testing of cryptographic primitives, in
  the form of a Python library and CLI. It is complemented by an extensive
  documentation, featuring guides on the primitives supported.

  **Q: What is a cryptographic primitive?**

  **A**: A low-level cryptographic algorithm, generally used to build a protocol.
  For example, AES is an encryption primitive that is used in the TLS protocol,
  which is the protocol your browser used to securely get this page.

  **Q: What is compliance testing?**

  **A**: Algorithms are described in specifications, such as FIPS publications or
  RFCs. When implementing these algorithms, we want to ensure that they comply
  with the specification, i.e. the implementation behaves as the algorithm
  described.

  **Q: How to test for compliance then?**

  **A**: We can use *test vectors*, which are sets of inputs and their
  corresponding outputs. For example, encrypting with AES is a deterministic
  operation: for a given key and message, AES will always return the same
  ciphertext. So we can choose some input values, run the algorithm, and record
  the value returned. All implementations of AES are then expected to return the
  *same* ciphertext for this given key and message. If it does not, then it is not
  compliant.

  **Q: And so, what does crypto-condor do?**

  **A**: crypto-condor provides both a nice Python API and a wrapper system to
  test implementations with sets of test vectors that come from sources such as
  the [NIST
  CAVP](https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program).

  The Python API exposes test functions that take an implementation as input, in
  the form of a Python function or class, passes the inputs defined by the test
  vectors to that implementation, and checks if the outputs are those defined by
  the vectors.

  The wrappers are small programs that already define the function prototype. The
  user calls the implementation to test inside this function, and crypto-condor
  runs it with the test vectors as with the Python API.

  And it comes with a documentation, wrapper examples, and guides on supported
  primitives.



  crypto-condor requires **Python 3.10+**. For information, it is developed using
  Python 3.12 on Fedora 39.

  The implementations of **AES, Kyber, Dilithium, and TestU01** are written in C
  and are compiled directly on the user's machine. As such, they require a C
  compiler and GNU Make. These primitives are only compiled when required, and not
  when installing the package:

  - AES: when testing the output of an implementation using classic modes of
  operation (not CCM or GCM).
  - Kyber: when testing the output of an implementation **or** when using test
  vectors on the `encapsulate` function.
  - Dilithium: when testing the output of an implementation.
  - TestU01: when used to test a file.



  It is available on PyPI:

  ```bash
  python -m pip install crypto-condor
  ```

  An up-to-date list of the requirements can be found in the
  `[tool.poetry.dependencies]` section of the
  [pyproject.toml](https://github.com/quarkslab/crypto-condor/blob/main/pyproject.toml)
  file.



  > The documentation is available at
  > <https://quarkslab.github.io/crypto-condor/latest/index.html>.

  Once installed, the CLI is available as `crypto-condor-cli`. It is structured in
  commands, similar to Git. Run it without arguments or with `--help` to display
  the help message detailing the available subcommands. You can check [the
  documentation](https://quarkslab.github.io/crypto-condor/latest/index.html) for
  a quick rundown of all the commands.

  As for the Python library, it is available as `crypto_condor` (note the
  underscore). Each primitive has its own module under `primitives`, e.g.
  `crypto_condor.primitives.AES`. It contains the functions used to test
  implementations.



  See
  [CONTRIBUTING](https://github.com/quarkslab/crypto-condor/blob/main/CONTRIBUTING.md).



  A [changelog](https://github.com/quarkslab/crypto-condor/blob/main/CHANGELOG.md)
  is available. This projects adheres to [CalVer](https://calver.org/). The format
  used is YYYY.MM.DD\[.MICRO\]\[-MODIFIER\]:

  - YYYY: full year (2023).
  - 0M: zero-padded month (01, 02, ..., 12).
  - 0D: zero-padded day (01, 02, ..., 31).
  - MICRO: an increasing counter, used for patches published in the same day.
  - MODIFIER: usually `rc<n>` to indicate a release candidate.



  - Julio Loayza Meneses, Quarkslab.
  - Angèle Bossuat, Quarkslab.
  - Dahmun Goudarzi, Quarkslab.

  Logo idea by Robin David, drawing by Irene Loayza.



  [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0), see
  [LICENSE](https://github.com/quarkslab/crypto-condor/blob/main/LICENSE).


pkgId: 3fedf9bb2e19c58346ce115730d9486ab35ad695b5f68ce3d592c1364361c5e8
name: python-crypto-connector
arch: src
version: 0.4.1
epoch: 0
release: 1
summary: A lightweight library that works as a connector to several crypto exchanges APIs.
url: https://github.com/Izem0/crypto-connector
rpm_license: None
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-connector-0.4.1-1.src.rpm
checksum_type: sha256
description:
  ![tests](https://github.com/Izem0/crypto-connector/actions/workflows/tests.yml/badge.svg)
  [![PyPI version](https://img.shields.io/pypi/v/crypto-connector)](https://pypi.python.org/pypi/crypto-connector)
  [![Python version](https://img.shields.io/pypi/pyversions/crypto-connector)](https://www.python.org/downloads/)
  [![Code Style](https://img.shields.io/badge/code_style-black-black)](https://black.readthedocs.io/en/stable/)
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)


  This is a lightweight library that works as a connector to Binance SPOT and HTX SPOT APIs.

  - Supported APIs:
      - [Binance SPOT API](https://developers.binance.com/docs/binance-spot-api-docs/rest-api)
      - [HTX SPOT API](https://www.htx.com/en-in/opend/newApiPages/)



  ```bash
  pip install crypto-connector
  ```




  ```python
  import crypto_connector as cc

  exc = cc.Binance()



  print(exc.get_server_time())


  print(exc.klines("ETHUSDT", "1d"))
  ```


  API key/secret are required for private endpoints.
  ```python
  import crypto_connector as cc





  exc = cc.Binance(
      sub_api_key="",
      sub_api_secret="",
      sub_email="",
      master_api_key="",
      master_api_secret="",
  )






  print(exc.get_api_key_info())


  print(exc.get_balance())


  order = exc.place_order("ETHUSDT", type="limit", side="buy", qty=0.015, price=1000)
  print(order)


  print(exc.get_open_orders())
  ```



  - This library is not intended to be comprehensive, I use it mainly for my personal projects
  - Not all endpoints of the different APIs are supported
  - Websocket and Futures are not supported


pkgId: c522ca60855227d7edf7554c8e43d071b978484d37126ee61c855f6ab96c2448
name: python-crypto-exchange-api
arch: src
version: 0.1.8
epoch: 0
release: 1
summary: A Python framework for accessing multiple cryptocurrency exchanges APIs
url: https://github.com/hedeqiang/python_crypto_exchange_api
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-exchange-api-0.1.8-1.src.rpm
checksum_type: sha256
description:
  A Python framework for accessing multiple cryptocurrency exchanges' APIs.


  - Binance
  - OKX
  - Bitget
  - Kucoin
  - MEXC
  - Gate.io
  - Bybit
  - Kraken
  - BTSE



  ```bash
  pip install crypto_exchange_api
  ```


  First, import the necessary modules and set up your API configurations:
  ```python
  from crypto_exchange import CryptoAPIManager, ExchangeConfig, ExchangeName


  manager = CryptoAPIManager()


  binance_config = ExchangeConfig(api_key='your_binance_api_key', api_secret='your_binance_api_secret')
  okx_config = ExchangeConfig(api_key='your_okx_api_key', api_secret='your_okx_api_secret', api_passphrase='your_okx_api_passphrase')
  bitget_config = ExchangeConfig(api_key='your_bitget_api_key', api_secret='your_bitget_api_secret', api_passphrase='your_bitget_api_passphrase')
  kucoin_config = ExchangeConfig(api_key='your_kucoin_api_key', api_secret='your_kucoin_api_secret', api_passphrase='your_kucoin_api_passphrase')
  mexc_config = ExchangeConfig(api_key='your_mexc_api_key', api_secret='your_mexc_api_secret')
  gateio_config = ExchangeConfig(api_key='your_gateio_api_key', api_secret='your_gateio_api_secret')
  bybit_config = ExchangeConfig(api_key='your_bybit_api_key', api_secret='your_bybit_api_secret')
  kraken_config = ExchangeConfig(api_key='your_kraken_api_key', api_secret='your_kraken_api_secret')


  binance = manager.add_exchange(ExchangeName.BINANCE, binance_config).get_exchange()
  okx = manager.add_exchange(ExchangeName.OKX, okx_config).get_exchange()
  bitget = manager.add_exchange(ExchangeName.BITGET, bitget_config).get_exchange()
  kucoin= manager.add_exchange(ExchangeName.KUCOIN, kucoin_config).get_exchange()
  mexc = manager.add_exchange(ExchangeName.MEXC, mexc_config).get_exchange()
  gate = manager.add_exchange(ExchangeName.GATEIO, gateio_config).get_exchange()
  bybit = manager.add_exchange(ExchangeName.BYBIT, bybit_config).get_exchange()
  kraken = manager.add_exchange(ExchangeName.KRAKEN,kraken_config).get_exchange()
  ```

  > The `signed` parameter indicates whether the request requires authentication.
  > Public endpoints (such as market data) do not require authentication and can be accessed with signed=False.
  > Private endpoints (such as account data or trading) require authentication and must be accessed with signed=True.




  ```python

  response = binance.send_request( 'GET', '/api/v3/ticker/price', params={'symbol': 'BTCUSDT'}, signed=False)
  print(response)
  ```


  ```python

  params = {
      'coin': 'USDT',
      'withdrawOrderId': '123456',
      'amount': 10,
      'network': 'BSC',
      'address': 'your_usdt_address'
  }
  response = binance.send_request('POST', '/sapi/v1/capital/withdraw/apply', params=params, signed=True)
  print(response)

  ```


  ```python

  response = okx.send_request('GET', '/api/v5/market/ticker', params={'instId': 'BTC-USDT'}, signed=False)
  print(response)
  ```


  ```python

  params = {
      'ccy': 'USDT',
      'amt': '10',
      'dest': '4',  # 4 means withdrawal to external address
      'toAddr': 'your_usdt_address',
      'fee': '0.5'
  }
  response = okx.send_request('POST', '/api/v5/asset/withdrawal', params=params, signed=True)
  print(response)
  ```



  ```python

  response = bitget.send_request('GET', '/api/v2/spot/market/tickers', params={'symbol': 'BTCUSDT'}, signed=False)
  print(response)
  ```


  ```python

  params = {
      'coin': 'USDT',
      'address': 'your_usdt_address',
      'size': '10',
      'tag': '',
      'chain': 'TRX',
      'clientOid': '123456'
  }
  response = bitget.send_request('POST', '/api/v2/spot/wallet/withdrawal', params=params, signed=True)
  print(response)
  ```



  ```python

  response = kucoin.send_request('GET', '/api/v1/market/allTickers', signed=False)
  print(response)
  ```


  ```python

  params = {
      'currency': 'BTC',
      'address': 'your_btc_address',
      'amount': '0.01',
      'memo': '',
      'chain': 'BTC'
  }
  response = kucoin.send_request('POST', '/api/v1/withdrawals', params=params, signed=True)
  print(response)
  ```



  ```python

  response = mexc.send_request('GET', '/api/v3/ticker/price', params={'symbol': 'BTCUSDT'}, signed=False)
  print(response)
  ```


  ```python

  params = {
      'coin': 'BTC',
      'withdrawOrderId': 'xxxx',
      'address': 'your_btc_address',
      'amount': '0.01',
      'netWork': 'BTC'
  }
  response = mexc.send_request('POST', '/api/v3/capital/withdraw', params=params, signed=True)
  print(response)
  ```



  ```python

  response = gate.send_request('GET', 'v4/spot/currency_pairs/BTC_USDT', signed=False)
  print(response)
  ```

  ```python

  params = {
      'withdraw_order_id':'123456',
      'currency': 'BTC',
      'address': 'your_btc_address',
      'amount': '0.01',
      'chain': 'BTC',
  }
  response = gate.send_request('POST', '/api/v4/withdrawals', params=params, signed=True)
  print(response)
  ```



  ```python

  response = bybit.send_request('GET', '/v5/market/tickers', params={'symbol': 'BTCUSDT', 'category': 'spot'},
                                    signed=False)
  print(response)
  ```


  ```python

  params = {
      'coin': 'BTC',
      'chain': 'BTC',
      'address': 'your_btc_address',
      'amount': '0.01',
      'timestamp': 1672196561407,
      'forceChain': 0,
      'accountType': 'FUND'
  }
  response = bybit.send_request('POST', '/v5/asset/withdraw/create', params=params, signed=True)
  print(response)
  ```





  ```python

  response = kraken.send_request('GET', '/0/public/Ticker', params={'pair': 'XBTUSD'}, signed=False)
  print(response)
  ```


  ```python

  params = {
      'asset': 'BTC',
      'key': 'your_withdrawal_key',
      'amount': '0.01',
      'address':'your_btc_address'
  }
  response = kraken.send_request('POST', '/0/private/Withdraw', params=params, signed=True)
  print(response)
  ```
  ...


  This project is licensed under the MIT License.


pkgId: b0a3422f99364d0a7067279271974190e16af0681f0893d12a4a00a692aecc1c
name: python-crypto-js-to-py
arch: src
version: 0.2.2
epoch: 0
release: 1
summary: 基于pycryptodomex的二次封装,对CryptoJS的AES和CBC进行了简单的复刻
url: https://pypi.org/project/crypto-js-to-py/
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-js-to-py-0.2.2-1.src.rpm
checksum_type: sha256
description:
  这是一个crypto-js的Python复刻版,对ECB和CBC进行了简单的复刻


  ```bash
  pip install crypto-js-to-py
  ```
  ```python
  from crypto_py import CryptoPY
  ```


  * 一个简单的crypto-js移植python的示例代码
    * 这是一段crypto-js的示例代码
        ```js
        const CryptoJS = require("crypto-js")

        key = CryptoJS.enc.Utf8.parse("1234567890123456")
        iv = CryptoJS.enc.Utf8.parse("1234567890123456")
        data = "我是帅哥"

        encrypt = CryptoJS.AES.encrypt(data,key,{
            "iv": iv,
            "mode":CryptoJS.mode.CBC,
            "padding":CryptoJS.pad.Pkcs7
        }).toString()

        console.log(encrypt)
        ```

    * 这是移植到python的代码
        ```python

        from crypto_py import CryptoPY as CryptoJS

        
        key = CryptoJS.enc.Utf8.parse("1234567890123456")
        iv = CryptoJS.enc.Utf8.parse("1234567890123456")
        data = "我是帅哥"

        
        
        
        
        
        
        


        encrypt = CryptoJS.AES.encrypt(data,key,{
            "iv": iv,
            "mode":CryptoJS.mode.CBC,
            "padding":CryptoJS.pad.Pkcs7
        }).decode()

        print(encrypt)


        ```

  * 同时也支持NoPadding和ciphertext
     * `CryptoPY.pad.NoPadding` <font color="red">暂时未能测试是否无BUG</font>
     * `CryptoPY.AES.encrypt(...).ciphertext.decode()`
     * `CryptoPY.AES.decrypt(...).ciphertext.decode()`


pkgId: 8e8963584fd9161d475e1ae5498f34d108ec9cc192343e2c94cdbcb0124cd4f5
name: python-crypto-licensing
arch: src
version: 4.2.0
epoch: 0
release: 1
url: https://github.com/pjkundert/crypto-licensing
rpm_license: Dual License; GPLv3 and Proprietary
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-licensing-4.2.0-1.src.rpm
checksum_type: sha256
summary:
  The crypto-licensing module implements Ed25519-signed license checking and automatic issuance after cryptocurrency payment

description:
  Licensing software and getting paid for it has become extremely difficult, due to government
  regulatory and banking interference.

  Using crypto-licensing allows you automatically and securely issue licenses, and get paid in various
  cryptocurrencies.


pkgId: 4105c1bac2e5ca2f6e4b38439510aea4eec5f9fa91d43559029ea51b7c6bd074
name: python-crypto-screening
arch: src
version: 8.20.0
epoch: 0
release: 1
summary: A software for automatically recording real-time crypto exchanges and pairs data.
url: https://github.com/Shahaf-F-S/crypto-screening
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-screening-8.20.0-1.src.rpm
checksum_type: sha256
description:
  - written and owned by: Shahaf Frank-Shapir
  - all the rights are saved for Shahaf Frank-Shapir
  - programming languages: python 3.9.12 (100%)


pkgId: d1367ad6fe63b032f99d95d6bd59b3f9030fd18e745b74431931de9fcb949d80
name: python-crypto-strategy
arch: src
version: 0.1.20
epoch: 0
release: 1
summary: Repo to create crypto strategies
url: https://github.com/minggnim/crypto-strategy
rpm_license: GNU General Public License v3 (GPLv3)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-strategy-0.1.20-1.src.rpm
checksum_type: sha256
description:
  A repository to perform backtests and create trading strategies for cryptocurrencies.

  [![Dependency Review](https://github.com/minggnim/crypto-strategy/actions/workflows/dependency-review.yml/badge.svg)](https://github.com/minggnim/crypto-strategy/actions/workflows/dependency-review.yml)
  [![Python package](https://github.com/minggnim/crypto-strategy/actions/workflows/python-package.yml/badge.svg)](https://github.com/minggnim/crypto-strategy/actions/workflows/python-package.yml)
  [![pypi-upload](https://github.com/minggnim/crypto-strategy/actions/workflows/python-publish.yml/badge.svg)](https://github.com/minggnim/crypto-strategy/actions/workflows/python-publish.yml)

  ![](./img/algo-trading.png)



  ```
  pip install crypto-strategy[full]
  ```




  1. Moving Average Strategy

  - Description
      ```
      BestMaStrategy(symbols, freq, res_dir, flag_filter, flag_stop)
      ```
      - symbols: asset name, e.g., BTCUSDT
      - freq: data frequency to use, 1h | 4h
      - res_dir: results directory
      - flag_filter: filter to use, [mmi | ang]
          - mmi: Market Meanness Index filter
          - ang: Linear Regression Angle filter
      - flag_stop: early stop flag, [ts_stop | sl_stop | tp_stop]
          - ts_stop: trailing stop
          - sl_stop: stop loss
          - tp_stop: take profit

  - Example: Find the best params using 1h data with mmi filter and ts_stop
      ```
      BestMaStrategy(
          symbols=['BTCUSDT', 'ETHUSDT'],
          freq='1h',
          res_dir='results/best-1h-ma-mmi-filter',
          flag_filters='mmi',
          flag_stop='ts_stop'
      )
      ```


  2. Breakout Strategy
      - Description: A method to optimize the BO strategy
          - symbols: a list of symbols to be optimzied on, e.g., 'BTCUSDT'
          - freq: currently supported values: '1h' or '4h'
          - res_dir: the output directory
          - flag_filter: currently supported filters: 'vol', 'ang', default: None
          - flag_stop: early stop flag, [ts_stop | sl_stop | tp_stop]
              - ts_stop: trailing stop
              - sl_stop: stop loss
              - tp_stop: take profit


      - Example: Find the best params for bo strategy with vol filter using BTCUSDT 4h data and sl_stop
          ```
          BestBoStrategy(
              symbols = 'BTCUSDT',
              freq = '4h',
              res_dir = 'results/best-4h-bo_rev-vol-filter',
              flag_filters = 'vol',
              flag_stop = 'sl_stop',
          )


  3. MACD Strategy
  ```
  BestMacdStrategy(symbols, freq, res_dir, flag_filter)
  ```
  - symbols: asset name, e.g., BTCUSDT
  - freq: data frequency to use, 1h | 4h
  - res_dir: results directory
  - flag_filter: filter to use, [mmi | ang | stoch | sma]
      - vol: Volume filter
      - ang: Linear Regression Angle filter




  1. MA Strategy

      - Description: A method to inspect the MA strategy with given params
          ```
          InspectMaStrategy(symbols, freq, name, n1, n2, timeperiod, threshold, flag_filter, stop_vars)
          ```
          - symbol: the name of the crypto, e.g., 'BTCUSDT'
          - freq: currently supported values are '1h' or '4h'
          - name, n1, n2: the name and the params of the ma strategy, e.g., 'sma', 100, 50
          - timeperiod: param used in either mmi or ang filter
          - threshold: param used in ang filter
          - flag_filter: currently supported fitlers: 'mmi', 'ang', default: None
          - stop_vars: dictionary of stop vars, currently support 'ts_stop', 'sl_stop', 'tp_stop', default None
      - Example: Inspect MA strategy `linear_reg` with `n1=30 & n2=280` and `sl_stop=0.1 & tp_stop=0.1`
          ```
          InspectMaStrategy(
              symbols,
              freq='4h',
              name='linear_reg', n1=30, n2=280,
              stop_vars={'sl_stop':0.1, 'tp_stop':0.1})
          ```

  2. BO Strategy

      - Description: A method to inspect the BO strategy
          ```
          InspectBoStrategy(symbol, freq, long_window, short_window, ts_stop, timeperiod, multiplier, threshold, flag_filter, flag_ts_stop)
          ```
          - symbols: a list of symbols to be optimzied on, e.g., 'BTCUSDT'
          - freq: currently supported values: '1h' or '4h'
          - long_window, short_window: breakout params
          - flag_filter: currently supported fitlers: 'vol', 'ang', default: None
          - timeperiod, multiplier: volume filter params
          - timeperiod, threshold: angle filter params
          - stop_vars: dictionary of stop vars, currently support 'ts_stop', 'sl_stop', 'tp_stop', default None

      - Example: Inspect 4h BTCUSDT breakout strategy with volume filter and trailing stop
          ```
          InspectBoStrategy(
              'BTCUSDT',
              freq='4h',
              long_window=100, short_window=50,
              flag_filter='vol', timeperiod=20, multiplier=2,
              stop_vars={'ts_stop':0.1})
          ```




  Backtests can also be carried out in command line. To find out more

  ```
  crypto --help
  ```

  Example 1: Find the best params for BO strategy with vol filter using 4h data
  ```
  crypto best-bo-strategy -f 4h -r results/best-4h-bo-vol-filter -g vol -e bo
  ```

  Example 2: Find the best params for MA strategy with mmi filter and ts_stop using 1h data
  ```
  crypto best-ma-strategy -f 1h -r results/best-1h-ma-mmi-filter -g mmi -t ts_stop
  ```


  pytest


pkgId: 78f94d4e5e7d361091dfaf1ff9056abc209d454a9d928d92d8ac6b9e002ba3aa
name: python-crypto-ws-api
arch: src
version: 2.0.14
epoch: 0
release: 1
summary: Crypto WS API connector for ASYNC requests
url: https://pypi.org/project/crypto-ws-api/
rpm_license: None
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto-ws-api-2.0.14-1.src.rpm
checksum_type: sha256
description:
  <h1 align="center"><img align="center" src="https://user-images.githubusercontent.com/77513676/250364389-cbedc171-a930-4467-a0cd-21627a6a41ed.svg" width="75">Crypto WS API connector for ASYNC requests</h1>

  <h2 align="center">Full coverage of all methods provided by the interface</h2>

  <h3 align="center">Provides of connection management, keepalive and rate limits control</h3>

  ***
  <a href="https://pypi.org/project/crypto-ws-api/"><img src="https://img.shields.io/pypi/v/crypto-ws-api" alt="PyPI version"></a>
  <a href="https://codeclimate.com/github/DogsTailFarmer/crypto-ws-api/maintainability"><img src="https://api.codeclimate.com/v1/badges/2d2a654ba393eb88d911/maintainability" /></a>
  <a href="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api.svg/?label=resolved+issues&token=TXghPzbi0YWhkCLU8Q1tmDyQ"/></a>
  <a href="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api.svg/?label=active+issues&token=TXghPzbi0YWhkCLU8Q1tmDyQ"/></a>
  <a href="https://sonarcloud.io/summary/new_code?id=DogsTailFarmer_crypto-ws-api" target="_blank"><img alt="sonarcloud" title="sonarcloud" src="https://sonarcloud.io/api/project_badges/measure?project=DogsTailFarmer_crypto-ws-api&metric=alert_status"/></a>
  <a href="https://pepy.tech/project/crypto-ws-api" target="_blank"><img alt="Downloads" title="Downloads" src="https://static.pepy.tech/badge/crypto-ws-api"/></a>
  ***
  For :heavy_check_mark:Binance, :heavy_check_mark:OKX, :heavy_check_mark:Bitfinex
  ***


  Lightweight and efficient solution to utilize of all available methods** provided through the:
  * [Binance Websocket API v3](https://binance-docs.github.io/apidocs/websocket_api/en)
  * [OKX Websocket API v5](https://www.okx.com/docs-v5/en/#overview-websocket)
  * [Bitfinex Websocket Inputs](https://docs.bitfinex.com/reference/ws-auth-input)

  ** Not for channel by one-way subscription, for **_request <-> response_** mode only


  - Credentials
  - Connection
  - Keepalive
  - Error handling
  - Limits control
  - Methods construction
    + Creating request on-the-fly from method name and params: {}
    + Generating signature if necessary
    + Response handling
  - logging


  - Start session instance
  - Send async request
  - Get response or raised exception
  - Reuse instance for different type requests
  - Stop session instance




  ```console
  pip install crypto_ws_api
  ```
  For upgrade to latest versions use:
  ```console
  pip install -U crypto_ws_api
  ```

  After first install create environment by run
  ```console
  crypto_ws_api_init
  ```
  in terminal window.

  The config directory will be created. You get path to config:

  >ubuntu@ubuntu:~$ crypto_ws_api_init
  >
  >Can't find config file! Creating it...
  >
  >Before first run set account(s) API key into /home/ubuntu/.config/crypto_ws_api/ws_api.toml


  * For test purpose log in at [Binance Spot Test Network](https://testnet.binance.vision/)

  For OKX and Bitfinex, unlike Binance, a limited number of methods are implemented at the WS API level,
  such as creating, modifying, and deleting orders.
  There are no public get-time (), ping-pong, etc., so this demo script is limited to calling Binance
  to demonstrate capabilities.

  * Create API Key
  * After install and create environment specify api_key and api_secret to the config file


  * Run in terminal window
  ```
  crypto_ws_api_demo
  ```



  _*`crypto_ws_api/demo.py` - complete and fully functional example*_



  ```bazaar
  from crypto_ws_api.ws_session import UserWSSession



  exchange, _test_net, api_key, api_secret, passphrase, ws_api_endpoint = get_credentials(account_name)

  session = aiohttp.ClientSession()

  trade_id = shortuuid.uuid()

  user_session = UserWSSession(
      session,
      exchange,
      ws_api_endpoint,
      api_key,
      api_secret,
      passphrase
  )
  ```


  ```bazaar
  async def account_information(user_session: UserWSSession, _trade_id):
      
      try:
          res = await user_session.handle_request(
              _trade_id,
              "account.status",
              _api_key=True,
              _signed=True
          )
          if res is None:
              logger.warning("Here handling state Out-of-Service")
      except asyncio.CancelledError:
          pass  # Task cancellation should not be logged as an error
      except Exception as _ex:
          logger.error(f"Handling exception: {_ex}")
      else:
          logger.info(f"Account information (USER_DATA) response: {res}")
  ```


  ```bazaar
  await account_information(user_session, trade_id)
  ```


  ```bazaar
  await user_session.stop()
  await session.close()
  ```


  ```bazaar
  if self.exchange == 'binance':
      params = {
          "symbol": "BTCUSDT",
          "side": "SELL",
          "type": "LIMIT",
          "timeInForce": "GTC",
          "price": "23416.10000000",
          "quantity": "0.00847000",
      }
      binance_res = await user_session.handle_request(trade_id, "order.place", params, _api_key=True, _signed=True)

  elif self.exchange == 'bitfinex':
      params = {
          "type": "EXCHANGE LIMIT",
          "symbol": "tBTCUSDT",
          "price": "23416.10000000",
          "amount": ('' if side == 'BUY' else '-') + "0.00847000",
      }
      bitfnex_res = await user_session.handle_request(trade_id, "on", params)

  elif self.exchange == 'okx':
      params = {
          "instId": "BTC-USDT",
          "tdMode": "cash",
          "clOrdId": "client_order_id",
          "side": "buy",
          "ordType": "limit",
          "sz": "0.00847000",
          "px": "23416.10000000",
      }
      okx_res = await user_session.handle_request(trade_id, "order", params)
  ```


  For configure logging in multi-module project use next snippet for yours `main()`:
  ```bazaar
  import logging.handlers

  logger = logging.getLogger(__name__)
  formatter = logging.Formatter(fmt="[s: s] s")

  sh = logging.StreamHandler()
  sh.setFormatter(formatter)
  sh.setLevel(logging.DEBUG)

  root_logger = logging.getLogger()
  root_logger.setLevel(logging.DEBUG)
  root_logger.addHandler(sh)
  ```


  Upon reaching the limit threshold of each type, the session switches to the Out-of-Service state.
  If you send a request in this state, the answer will be `None`

  *In any case, you are protected from exceeding limits and blocking for this reason*


  *USDT* (TRC20) TU3kagV9kxbjuUmEi6bUym5MTXjeM7Tm8K


pkgId: c57f68967ed18bed22589f5adb34397a73135f3953e3047dcf71d433e3dd6b25
name: python-crypto_plus
arch: src
version: 1.0.6
epoch: 0
release: 1
summary: A Easy-to-use Crypto Tool
url: https://pypi.org/project/crypto_plus/
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crypto_plus-1.0.6-1.src.rpm
checksum_type: sha256
description:
  一个易用的加解密、签名、证书工具。
  目前已发布到[pypi](https://pypi.org/project/crypto_plus)。






  ```bash
  pip install crypto_plus
  ```



  ```python
  from crypto_plus import CryptoPlus


  rsa = CryptoPlus.generate_rsa()




  plaintext = b'plaintext bytes'
  ciphertext = rsa.encrypt(plaintext)
  assert rsa.decrypt(ciphertext) == ciphertext


  message = b'message bytes'
  signature = rsa.sign(message)
  rsa.verify(message, signature)


  rsa.dump_cert(subject_name="subject", issuer_name="issuer")


  rsa.dump()
  rsa_from_file = CryptoPlus.load()

  ```



  阅读 [开发手册](./docs/development.md).


pkgId: 5312e1bb811675ed663d5a32205cc9c6e668b969343aee8c8b72071a0c150352
name: python-cryptoaddress
arch: src
version: 0.2.1
epoch: 0
release: 1
summary: Python library to verify crypto addresses.
url: https://pypi.org/project/cryptoaddress/
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptoaddress-0.2.1-1.src.rpm
checksum_type: sha256
description:
  Simple crypto address validator. Currently it supports the following crypto addresses:

  * Bitcoin
  * Ethereum
  * Litecoin




  Only supported in `python >= 3.5`.




  ```bash
  $ pip install cryptoaddress
  ```






  **BitcoinAddress(address [, network_type='mainnet'])**

  _Parameters_

  * `address`: the Bitcoin address to validate.
  * `network_type`: Optional. Use either 'mainnet' or 'testnet'.

  > Raises `ValueError` if the provided address is not a valid Bitcoin address




  **EthereumAddress(address)**

  _Parameters_

  * `address`: the Ethereum address to validate.

  > Raises `ValueError` if the provided address is not a valid Ethereum address




  **LitecoinAddress(address [, network_type='mainnet'])**

  _Parameters_

  * `address`: the Litecoin address to validate.
  * `network_type`: Optional. Use either 'mainnet' or 'testnet'.

  > Raises `ValueError` if the provided address is not a valid Litecoin address




  You can use directly a subclass of `CryptoAddress`:

  ```python
  from cryptoaddress import BitcoinAddress

  try:
      bitcoin_address = BitcoinAddress('17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem', network_type='mainnet')
      print('The address "%s" is valid.' % str(bitcoin_address))
  except ValueError:
      print('The address is invalid.')


  ```

  Or you can use the `get_crypto_address` utility function:

  ```python
  from cryptoaddress import get_crypto_address

  try:
      bitcoin_address = get_crypto_address('BTC', '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem', network_type='mainnet')
      print('The address "%s" is valid.' % str(bitcoin_address))
  except ValueError:
      print('The address is invalid.')


  ```




  After creating a semantic versioning tag proceed in the following way:

  * Install all required packages:

  ```bash
  $ python3 -m pip install --user --upgrade setuptools wheel twine
  ```

  * Generate the package:

  ```bash
  $ python3 setup.py sdist bdist_wheel
  ```

  This will create a `dist/` folder whose content has to be uploaded.


  * Upload the new package to pypi:

  ```bash
  $ python3 -m twine upload dist/*
  ````


pkgId: 15855c4a83818ada783ded82af291c04f4b8abaab92f767efbc77655557fb57f
name: python-cryptocode
arch: src
version: 0.1
epoch: 0
release: 1
summary: Python library used to encrypt and decrypt strings in the simplest possible way.
url: https://github.com/gdavid7/cryptocode
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptocode-0.1-1.src.rpm
checksum_type: sha256
description:
  Python library used to encrypt and decrypt strings in the simplest possible way, while also being incredibly secure.



  - **Python** 3 or later.



  Install some Python utilities along with some libraries and other stuff:

  ~~~
  pip install cryptocode
  ~~~


  Encrypting a message:

  ~~~
  >>> import cryptocode
  >>> myEncryptedMessage = cryptocode.encrypt("I like trains", "password123")
  >>> print(myEncryptedMessage)
  M+Wykmlub0z7FhEdmA==*PvAbXRNx0SiSDHHxLsKZ5w==*ihQM/fdkgrX3G+yOItyAUQ==*QFNDmuUP1ysgo01/P2MNpg==
  ~~~

  The first parameter is the string you want to encrypt. The second parameter is the password, which will be used for decrypting the string.

  Decrypting a message"
  ~~~
  >>> import cryptocode
  >>> myDecryptedMessage = cryptocode.decrypt("M+Wykmlub0z7FhEdmA==*PvAbXRNx0SiSDHHxLsKZ5w==*ihQM/fdkgrX3G+yOItyAUQ==*QFNDmuUP1ysgo01/P2MNpg==", "password123")
  >>> print(myDecryptedMessage)
  I like trains
  ~~~
  The first parameter is the encrypted string and the second parameter is the password. If the password is incorrect, decrypt function will return `False`.


  Here, we will be creating a simple "trial product key". This is useful if you have software that you would like people to use temporarily.
  In this example, we will be letting the user use the product for 2 hours. The password we will be using is ``cryptocode is amazing``.

  Code on the server side:
  ~~~
  import cryptocode
  import time
  hours = 2
  messageToEncrypt = str(time.time() + hours * 60 * 60)

  cryptocode.encrypt(messageToEncrypt, "cryptocode is amazing")
  ~~~

  Code on the client side:
  ~~~
  import cryptocode
  import time
  import sys

  def check_valid(key):
      message = cryptocode.decrypt(key, 'cryptocode is amazing')
      if message == False:
          
          return False
      if float(message) >= time.time():
          return True
      else:
          
          return False
  userKeyInput = input("Please enter your product key.")
  keyChecked = check_valid(userKeyInput)
  if keyChecked == True:
      print("You are good to go!")
  if keyChecked == False:
      print("You have either entered an invalid key or your time has expired. Sorry!")
      sys.exit()
  ~~~


pkgId: 7962fd4d583a8d88c2d2ad442e3564e710312ead14ddbb6442771082746ca22c
name: python-cryptocom-exchange
arch: src
version: 0.15.0
epoch: 0
release: 1
summary: Python 3.9+ async library for crypto.com/exchange API using httpx and websockets
url: https://pypi.org/project/cryptocom-exchange/
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptocom-exchange-0.15.0-1.src.rpm
checksum_type: sha256
description:
  [![Docs Build Status](https://readthedocs.org/projects/cryptocom-exchange/badge/?version=latest&style=flat)](https://readthedocs.org/projects/cryptocom-exchange)
  ![Test workflow](https://github.com/goincrypto/cryptocom-exchange/actions/workflows/test_release.yml/badge.svg)
  [![Maintainability](https://api.codeclimate.com/v1/badges/8d7ffdae54f3c6e86b5a/maintainability)](https://codeclimate.com/github/goincrypto/cryptocom-exchange/maintainability)
  [![Test Coverage](https://api.codeclimate.com/v1/badges/8d7ffdae54f3c6e86b5a/test_coverage)](https://codeclimate.com/github/goincrypto/cryptocom-exchange/test_coverage)
  [![PyPI implementation](https://img.shields.io/pypi/implementation/cryptocom-exchange.svg)](https://pypi.python.org/pypi/cryptocom-exchange/)
  [![PyPI pyversions](https://img.shields.io/pypi/pyversions/cryptocom-exchange.svg)](https://pypi.python.org/pypi/cryptocom-exchange/)
  [![PyPI license](https://img.shields.io/pypi/l/cryptocom-exchange.svg)](https://pypi.python.org/pypi/cryptocom-exchange/)
  [![PyPI version fury.io](https://badge.fury.io/py/cryptocom-exchange.svg)](https://pypi.python.org/pypi/cryptocom-exchange/)
  [![PyPI download month](https://img.shields.io/pypi/dm/cryptocom-exchange.svg)](https://pypi.python.org/pypi/cryptocom-exchange/)
  [![Gitter](https://badges.gitter.im/goincrypto/cryptocom-exchange.svg)](https://gitter.im/goincrypto/cryptocom-exchange?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

  Documentation: [https://cryptocom-exchange.rtfd.io](https://cryptocom-exchange.rtfd.io)

  Exchange original API docs: [https://exchange-docs.crypto.com](https://exchange-docs.crypto.com)



  `pip install cryptocom-exchange` or `poetry add cryptocom-exchange`

  - fully operational v1 exchange API, improved testing with replay mechanics
  - provides all methods to access crypto.com/exchange API including websockets
  - *important*: temporary disabled tests for withdrawals (soon)
  - full test coverage on real exchange with real money
  - simple async methods with custom retries and timeouts


pkgId: 7ce660521613ab18c63edfe65dafcf3f816dc18ab586bd69dc1a393d1c1005cf
name: python-cryptocompare
arch: src
version: 0.7.6
epoch: 0
release: 1
summary: Wrapper for CryptoCompare.com
url: https://github.com/lagerfeuer/cryptocompare
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptocompare-0.7.6-1.src.rpm
checksum_type: sha256
description:
  Python3 wrapper to query cryptocurrency prices (and more) using the [CryptoCompare](https://min-api.cryptocompare.com/) API.



  ```sh
  pip3 install cryptocompare
  ```



  ```python
  import cryptocompare
  ```



  If you have an API key, you can either set it as environment variable `CRYPTOCOMPARE_API_KEY` or set it manually with:

  ```
  cryptocompare.cryptocompare._set_api_key_parameter(KEY_HERE)
  ```



  ```python
  cryptocompare.get_coin_list(format=False)


















  ```

  If `format` is `True`, the coin list is returned as Python list,
  containing only the abbreviations (like `BTC`).



  ```python
  cryptocompare.get_price('BTC')

  cryptocompare.get_price('BTC', currency='USD', full=True)

  cryptocompare.get_price(['BTC', 'ETH'], ['EUR', 'GBP'])



  ```



  ```python

  cryptocompare.get_historical_price('XMR', timestamp=datetime.datetime(2017,6,6), exchange='CCCAGG')

  cryptocompare.get_historical_price('XMR', 'EUR', datetime.datetime(2017,6,6))


  ```



  ```python
  cryptocompare.get_historical_price_day('BTC', currency='EUR')
  cryptocompare.get_historical_price_day('BTC', currency='EUR', limit=30)
  cryptocompare.get_historical_price_day('BTC', 'EUR', limit=24, exchange='CCCAGG', toTs=datetime.datetime(2019,6,6))

  cryptocompare.get_historical_price_day('BTC', 'EUR', limit=24, exchange='CCCAGG', toTs=datetime.datetime(1559815200))
  ```



  ```python
  cryptocompare.get_historical_price_hour('BTC', currency='EUR')
  cryptocompare.get_historical_price_hour('BTC', currency='EUR', limit=24)
  cryptocompare.get_historical_price_hour('BTC', 'EUR', limit=24, exchange='CCCAGG')
  cryptocompare.get_historical_price_hour('BTC', 'EUR', limit=24, exchange='CCCAGG', toTs=datetime.datetime(2019,6,6,12))

  cryptocompare.get_historical_price_hour('BTC', 'EUR', limit=24, exchange='CCCAGG', toTs=datetime.datetime(1559815200))
  ```



  ```python
  cryptocompare.get_historical_price_minute('BTC', currency='EUR')
  cryptocompare.get_historical_price_minute('BTC', currency='EUR', limit=1440)
  cryptocompare.get_historical_price_minute('BTC', 'EUR', limit=24, exchange='CCCAGG', toTs=datetime.datetime.now())
  ```



  ```python
  cryptocompare.get_avg('BTC', currency='EUR', exchange='Kraken')



















  ```



  ```python
  cryptocompare.get_exchanges()
  ```



  ```python
  cryptocompare.get_pairs()
  pairs = cryptocompare.get_pairs(exchange='Kraken')
  ```



  Tests run assuming you have an API key, otherwise they will error due to rate limiting. Install the dev dependencies and run the tests:
  ```sh
  pip3 install -r requirements.txt
  python3 -m pytest
  ```



  * The [CryptoCompare API](https://min-api.cryptocompare.com/).



  This is a hobby project, no guarantees.
  If you find bugs or want additional features,
  open an issue and/or create a pull request.


pkgId: 5f28d6c478b17a19bdd3672809fdc139eba7e7e926a61153b0f976a500a50f03
name: python-cryptoconditions
arch: src
version: 0.8.1
epoch: 0
release: 1
url: https://github.com/bigchaindb/cryptoconditions/
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptoconditions-0.8.1-1.src.rpm
checksum_type: sha256
summary:
  Multi-algorithm, multi-level, multi-signature format for expressing conditions and fulfillments according to the Interledger Protocol (ILP).

description:
  A Python implementation of the Crypto-Conditions spec: a multi-algorithm, multi-level, multi-signature format for expressing conditions and fulfillments.
  This implementation doesn't implement the entire Crypto-Conditions spec. It implements the conditions needed by BigchainDB, and some others. It's compliant with `version 02 <https://tools.ietf.org/html/draft-thomas-crypto-conditions-02>`_ and `version 03 <https://tools.ietf.org/html/draft-thomas-crypto-conditions-02>`_ of the spec.
  See also: the `rfcs/crypto-conditions repository <https://github.com/rfcs/crypto-conditions>`_.


pkgId: f288e3f81b1386714b1402525a103d204e17cd8e1a976f60f4e7f9c6698ccdda
name: python-cryptocurrencyeda
arch: src
version: 0.3.9
epoch: 0
release: 1
summary: A package to analyze historical cryptocurrency prices and performance
url: https://pypi.org/project/cryptocurrencyeda/
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptocurrencyeda-0.3.9-1.src.rpm
checksum_type: sha256
description:
  [![ci-cd](https://github.com/UBC-MDS/cryptocurrencyeda/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/UBC-MDS/cryptocurrencyeda/actions/wokflows/ci-cd.yml)

  This is a Python package to analyze historical cryptocurrency prices and performance through simple exploratory data analysis including calculations and plotting. Data is sourced from the KuCoin API. There are four functions that are included in this python package which are described in more detail below. Cryptocurrency investors and enthusiasts can use this package to analyze cryptocurrencies of interest.


  There are existing Python libraries to access information of cryptocurrency such as [cryptocompare](https://github.com/lagerfeuer/cryptocompare) and [cryptofeed](https://github.com/bmoscon/cryptofeed). There are also existing Python libraries to visualize financial data such as [mplfinance](https://github.com/matplotlib/mplfinance).
  However, there is no integrated Python library for accessing, analyzing, and visualizing cryptocurrency data altogether. Therefore, we want to build a simple tool that can facilitate simple cryptocurrency data analysis all at once.



  The package contains the following four functions:

  - `retrieve_data`: downloads historical data from a cryptocurrency exchange using an an http request from a cryptocurrency exchange.

  - `plot_price`: generates and visualizes a plot of the price of the cryptocurrenty inputted over a period of time.

  - `daily_growth_rate`: performs calculation of daily growth rate of the cryptocurrenty inputted over a period of time.

  - `avg_daily_return`: performs calculation of the average daily return of the inputted cryptocurrency price.


  In order to use the package, please follow these steps:


  ```
  conda create --name cryptocurrencyeda python=3.9 -y
  ```

  ```
  conda activate cryptocurrencyeda
  ```

  ```
  pip install cryptocurrencyeda
  or
  pip install git+https://github.com/UBC-MDS/cryptocurrencyeda
  ```

  ```
  Python
  ```

  ```
  >>> from cryptocurrencyeda.retrieve_data import retrieve_data
  >>> from cryptocurrencyeda.plot_price import plot_price
  >>> from cryptocurrencyeda.avg_daily_return import avg_daily_return
  >>> from cryptocurrencyeda.daily_growth_rate import daily_growth_rate
  ```

  ```
  >>> retrieve_data(symbol="BTC-USDT",
                    time_period="1day",
                    start_date="2018-01-01",
                    end_date="2022-01-10",
                   )

  >>> plot_price(price_df)

  >>> daily_growth_rate(price_df, "Close")

  >>> avg_daily_return(price_df["Close"])
  ```



  The documentation is hosted on ReadTheDocs [here](https://cryptocurrencyeda.readthedocs.io/en/latest/)


  We welcome and recognize all contributions. You can see a list of current contributors in the [`contributors tab`](https://github.com/UBC-MDS/CryptocurrencyEDA/graphs/contributors). If you are interested in contributing to this project, please check out our CONDUCT.md

  - Berkay Bulut
  - Cici Du
  - Alex Yinan Guo
  - Nobby Nguyen



  `cryptocurrencyeda` was created by MDS Students from Group-11 for course 524. It is licensed under the terms of the MIT license.



  `cryptocurrencyeda` was created with [`cookiecutter`](https://cookiecutter.readthedocs.io/en/latest/) and the `py-pkgs-cookiecutter` [template](https://github.com/py-pkgs/py-pkgs-cookiecutter).


pkgId: 5cc036f1f88f6d0c733217f3ade4389f4ee3e9abd4eb799bd16153ef28b50226
name: python-cryptodatapy
arch: src
version: 0.2.8
epoch: 0
release: 1
summary: Cryptoasset data library
url: https://pypi.org/project/cryptodatapy/
rpm_license: Apache-2.0
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptodatapy-0.2.8-1.src.rpm
checksum_type: sha256
description:
  ![](cryptodatapy_logo.jpeg)



  <br/>

  **CryptoDataPy** is a python library which makes it easy to build high quality data pipelines
  for the analysis of digital assets. By providing easy access to over 100,000 time series for thousands of  assets,
  it facilitates the pre-processing of a wide range of data from different sources.

  Cryptoassets generate a huge amount of market, on-chain and off-chain data.
  But unlike legacy financial markets, this data is often fragmented,
  unstructured and dirty. By extracting data from various sources,
  pre-processing it into a user-friendly (tidy) format, detecting and repairing 'bad' data,
  and allowing for easy storage and retrieval, CryptoDataPy allows you to spend less time gathering
  and cleaning data, and more time analyzing it.

  Our data includes:

  - **Market:** market prices of varying granularity (e.g. tick, trade and bar data, aka OHLC),
  for spot, futures and options markets, as well as funding rates for the analysis of
  cryptoasset returns.
  - **On-chain:** network health and usage data, circulating supply, asset holder positions and
  cost-basis, for the analysis of underlying crypto network fundamentals.
  - **Off-chain:** news, social media, developer activity, web traffic and search for project interest and
  sentiment, as well as traditional financial market and macroeconomic data for broader financial and
  economic conditions.

  The library's intuitive interface facilitates each step of the ETL/ETL (extract-transform-load) process:

  - **Extract**: Extracting data from a wide range of data sources and file formats.
  - **Transform**:
    - Wrangling data into a pandas DataFrame in a structured and user-friendly format,
    a.k.a [tidy data](https://www.jstatsoft.org/article/view/v059i10).
    - Detecting, scrubbing and repairing 'bad' data (e.g. outliers, missing values, 0s, etc.) to improve the accuracy and reliability
  of machine learning/predictive models.
  - **Load**: Storing clean and ready-for-analysis data and metadata for easy access.



  ```bash
  $ pip install cryptodatapy
  ```



  **CryptoDataPy** allows you to pull ready-to-analyze data from a variety of sources
  with only a few lines of code.

  First specify which data you want with a `DataRequest`:

  ```python

  from cryptodatapy.extract.datarequest import DataRequest

  data_req = DataRequest(
      source='glassnode',  # name of data source
      tickers=['btc', 'eth'], # list of asset tickers, in CryptoDataPy format, defaults to 'btc'
      fields=['close', 'add_act', 'hashrate'],  # list of fields, in CryptoDataPy, defaults to 'close'
      freq=None,  # data frequency, defaults to daily
      quote_ccy=None,  # defaults to USD/USDT
      exch=None,  # defaults to exchange weighted average or Binance
      mkt_type= 'spot',  # defaults to spot
      start_date=None,  # defaults to start date for longest series
      end_date=None,  # defaults to most recent
      tz=None,  # defaults to UTC time
      cat=None,  # optional, should be specified when asset class is not crypto, eg. 'fx', 'rates', 'macro', etc.
  )
  ```
  Then get the data :

  ```python

  from cryptodatapy.extract.getdata import GetData

  GetData(data_req).get_series()
  ```

  With the same data request parameters, you can retrieve the same data from a different source:

  ```python

  data_req = DataRequest(
    source='coinmetrics',
    tickers=['btc', 'eth'],
    fields=['close', 'add_act', 'hashrate'],
    req='d',
    start_date='2016-01-01')

  GetData(data_req).get_series()
  ```

  For more detailed code examples and interactive tutorials
  see [here](https://github.com/systamental/cryptodatapy/blob/main/docs/example.ipynb).


  - [CryptoCompare](https://min-api.cryptocompare.com/documentation)
  - [CCXT](https://docs.ccxt.com/en/latest/)
  - [Glassnode](https://docs.glassnode.com/)
  - [Coin Metrics](https://docs.coinmetrics.io/api/v4/)
  - [Tiingo](https://api.tiingo.com/documentation/general/overview)
  - [Yahoo Finance](https://github.com/ranaroussi/yfinance)
  - [Fama-French Data](http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html)
  - [AQR](https://www.aqr.com/Insights/Datasets)
  - [Federal Reserve Economic Data (FRED)](https://fred.stlouisfed.org/docs/api/fred/)
  - [DBnomics](https://db.nomics.world/docs/)
  - [WorldBank](https://documents.worldbank.org/en/publication/documents-reports/api)
  - [Pandas-datareader](https://pandas-datareader.readthedocs.io/en/latest/)



  Interested in contributing? Check out the contributing guidelines and
  contact us at info@systamental.com. Please note that this project is s
  released with a Code of Conduct. By contributing to this project, you agree
  to abide by its terms.



  `cryptodatapy` was created by Systamental.
  It is licensed under the terms of the Apache License 2.0 license.


pkgId: 1653bb6edc6e7254f0b7649ab5630d96017d6ffc96076ef7c099a4e813d2fa0b
name: python-cryptography-suite
arch: src
version: 1.0.0
epoch: 0
release: 1
summary: A comprehensive and secure cryptographic toolkit.
url: https://github.com/Psychevus/cryptography-suite
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptography-suite-1.0.0-1.src.rpm
checksum_type: sha256
description:
  - **Comprehensive Functionality**: Access a wide array of cryptographic algorithms and protocols, including symmetric and asymmetric encryption, digital signatures, key management, secret sharing, password-authenticated key exchange (PAKE), and one-time passwords (OTP).
  - **High Security Standards**: Implements industry-leading algorithms with best practices, ensuring your data is safeguarded with the highest level of security.
  - **Developer-Friendly API**: Offers intuitive and well-documented APIs that simplify integration and accelerate development.
  - **Cross-Platform Compatibility**: Fully compatible with macOS, Linux, and Windows environments.


pkgId: 7922b1bf9cab2798ca8df92a64604dbeee547bbfe4702651e2ab3aaef454d72b
name: python-cryptojwt
arch: src
version: 1.9.2
epoch: 0
release: 1
summary: Python implementation of JWT, JWE, JWS and JWK
url: https://github.com/IdentityPython/JWTConnect-Python-CryptoJWT
rpm_license: Apache-2.0
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptojwt-1.9.2-1.src.rpm
checksum_type: sha256
description:
  ![License](https://img.shields.io/badge/license-Apache%202-blue.svg)
  ![Python version](https://img.shields.io/badge/python-3.7%7C%203.8%20%7C%203.9%20%7C%203.10%20%7C%203.11-blue.svg)
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

  An implementation of the JSON cryptographic specs JWS, JWE, JWK, and JWA [RFC 7515-7518] and JSON Web Token (JWT) [RFC 7519]

  Please read the [Official Documentation](https://cryptojwt.readthedocs.io/en/latest/) for getting usage examples and further informations.


pkgId: f1abb00689fa51d21dbdbfb4a4917c9ff5f1e3c97864e67f832e1052839f5181
name: python-cryptojwt
arch: src
version: 1.9.3
epoch: 0
release: 1
summary: Python implementation of JWT, JWE, JWS and JWK
url: https://github.com/IdentityPython/JWTConnect-Python-CryptoJWT
rpm_license: Apache-2.0
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptojwt-1.9.3-1.src.rpm
checksum_type: sha256
description:
  ![License](https://img.shields.io/badge/license-Apache%202-blue.svg)
  ![Python version](https://img.shields.io/badge/python-3.7%7C%203.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%203.12%203.13-blue.svg)
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

  An implementation of the JSON cryptographic specs JWS, JWE, JWK, and JWA [RFC 7515-7518] and JSON Web Token (JWT) [RFC 7519]

  Please read the [Official Documentation](https://cryptojwt.readthedocs.io/en/latest/) for getting usage examples and further informations.


pkgId: 6dd0afa598f9a70b3c3c6efd5f3f71b2af93060af6ca24d9b9b8cd3b1dbc5731
name: python-cryptol
arch: src
version: 3.2.1
epoch: 0
release: 1
summary: Cryptol client for the Cryptol RPC server
url: https://pypi.org/project/cryptol/
rpm_license: BSD License
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptol-3.2.1-1.src.rpm
checksum_type: sha256
description:
  In-development Python client for Cryptol. Currently tested on Linux and MacOS --
  at present there may be some minor issues running the Python Client in a Windows
  environment that need to be addressed (e.g., some Python process management methods
  are not cross-OS-compatible).

  This Cryptol client depends on the
  [cryptol-remote-api](https://github.com/GaloisInc/cryptol/tree/master/cryptol-remote-api)
  server.



  1. Clone the repo
  ```
  git clone https://github.com/GaloisInc/cryptol.git
  ```
  2. Enter the repo
  ```
  cd cryptol
  ```
  3. Initialize git submodules
  ```
  git submodule update --init
  ```
  4. Navigate to the python client
  ```
  cd cryptol-remote-api/python
  ```
  5. Install and setup some version of the `cryptol-remote-api` server and update any
     relevant environment variables as needed (see `cryptol.connect()` documentation
     for the various ways a server can be connected to).
     E.g., here is how the docker image of the server might be used:
  ```
  $ docker run --name=cryptol-remote-api -d \
    -v $PWD/tests/cryptol/test-files:/home/cryptol/tests/cryptol/test-files \
    -p 8080:8080 \
    ghcr.io/galoisinc/cryptol-remote-api:nightly-portable
  $ export CRYPTOL_SERVER_URL="http://localhost:8080/"
  ```
  6. Install the Python client (requires Python v3.7 or newer -- we recommend using [`poetry`](https://python-poetry.org/docs/#installation) to install the package):
  ```
  $ poetry install
  ```
  7. Run tests or individual scripts:
  ```
  $ poetry run python -m unittest discover tests/cryptol
  $ poetry run python tests/cryptol/test_salsa20.py
  ```



  First, clone the repository and submodules.

  ```
  $ git clone https://github.com/GaloisInc/cryptol.git
  $ cd cryptol
  $ git submodule update --init
  ```

  Then, use [`poetry`](https://python-poetry.org/docs/#installation) to install
  the python client from the `cryptol-remote-api/python` directory:

  ```
  $ cd cryptol-remote-api/python
  $ poetry install
  ```



  To run the verification scripts a `cryptol-remote-api` server must be available,
  either as a local executable or running in docker image listening on a port.



  Connecting to a server in a Python script is accomplished via the `cryptol.connect`
  method. Its accompanying Python doc strings describe the various ways it can be
  used. Below is a brief summary:

  `cryptol.connect()`, when provided no arguments, will attempt the following in order:

  1. If the environment variable ``CRYPTOL_SERVER`` is set and refers to an
     executable, it is assumed to be a `cryptol-remote-api` executable and will be
     used for the duration of the script.
  2. If the environment variable ``CRYPTOL_SERVER_URL`` is set, it is assumed to be
     the URL for a running Cryptol server in ``http`` mode and will be connected to.
     (N.B., this can be a local server or a server running in a docker image.)
  3. If an executable ``cryptol-remote-api`` is available on the ``PATH`` it is
      assumed to be a Cryptol server and will be used for the duration of the script.

  Additional arguments and options are documented with the function.

  Notably, the `reset_server` keyword can be used to connect to a running server
  and reset it, ensuring states from previous scripts have been cleared. E.g.,
  `cryptol.connect(reset_server=True)`.




  There are several ways a server executable can be obtained.



  An executable of the server is now included in each release of Cryptol.

  Nightly server builds can be found as `Artifacts` of the [Nightly
  Builds](https://github.com/GaloisInc/saw-script/actions/workflows/nightly.yml)
  github action. I.e., go to the `Nightly Builds` Github Action, click on a
  successful build, scroll to the bottom and under `Artifacts` a Linux, Windows,
  and MacOS tarball will be listed.

  Nightly Docker images of the server can be found under the
  [Packages](https://github.com/orgs/GaloisInc/packages?repo_name=cryptol) section
  of the Cryptol github repository.



  Release docker images for the Cryptol server are distributed with Cryptol
  releases; nightly Cryptol servers are available under the
  [Packages](https://github.com/orgs/GaloisInc/packages) section of the Cryptol repository.

  These images are set up to run as HTTP `cryptol-remote-api` servers, e.g.:

  ```
  docker run --name=cryptol-remote-api -d \
    -p 8080:8080 \
    ghcr.io/galoisinc/cryptol-remote-api:nightly-portable
  ```

  The `-v` option to `docker run` can be used to load files into the docker
  server's working directory so they can be loaded into the server at the request
  of python scripts. E.g., `-v PATH_TO_FILES:/home/cryptol/files/` will upload the
  contents of the host machine's directory `PATH_TO_FILES` to the
  `/home/cryptol/files/` directory in the docker container, which corresponds to the
  relative path `files/` for the Cryptol server. (If desired, it can be useful to
  place files in a location in the Docker container such that the same relative
  paths in scripts refer to the same files in the host machine and docker
  container.)



  If this repository is checked out and the build directions are successfully run,
  `cabal v2-exec which cryptol-remote-api` should indicate where the server executable has
  been stored by `cabal`.

  Alternatively `cabal v2-install cryptol-remote-api` should install the server
  executable into the user's `~/.cabal/bin` directory (or similar), which (if
  configured properly) should then appear as `cryptol-remote-api` in a user's `PATH`.




  Once the server is setup and any path variables are setup as desired, the
  Python (>= v3.7) client can be installed using
  [`poetry`](https://python-poetry.org/docs/#installation) as follows:

  ```
  $ cd cryptol-remote-api/python
  $ poetry install
  ```

  Then the tests or individual scripts can be run as follows:
  ```
  $ poetry run python -m unittest discover tests/cryptol
  $ poetry run python tests/cryptol/test_cryptol_api.py
  ```

  If leveraging environment variables is undesirable, the scripts themselves can
  specify a command to launch the server, e.g.:

  ```
  cryptol.connect(COMMAND)
  ```

  where `COMMAND` is a command to launch a new Cryptol server in socket mode.

  Or a server URL can be specified directly in the script, e.g.:

  ```
  cryptol.connect(url=URL)
  ```

  There is a step-by-step example [here](GettingStarted.md).



  To ensure any previous server state is cleared when running a Cryptol Python script
  against a persistent server (e.g., one running in HTTP mode in a different process),
  the `reset_server` keyword can be passed to `cryptol.connect()`. E.g.,

  ```
  cryptol.connect(url="http://localhost:8080/", reset_server=True)
  ```

  will connect to a Cryptol server running at `http://localhost:8080/` and will
  guarantee any previous state on the server is cleared.




  Currently, `cryptol-remote-api` officially supports python `3.12`. The `cryptol-remote-api` Docker image currently uses Python 3.10 and will be updated in issue #1661.


pkgId: b78c64df0867c25b3184cd8fcb07ecaa8649f9553614bc936daa8211048907cc
name: python-cryptonets-python-sdk
arch: src
version: 1.3.8
epoch: 0
release: 1
summary: Cryptonets SDK Library for Python
url: https://privateid.com/
rpm_license: None
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptonets-python-sdk-1.3.8-1.src.rpm
checksum_type: sha256
description:
  This package provides an interface to CryptoNets™ 1:N homomorphic token (HT) face recognition, Ultrapass™
  ID and human age estimation.



  Sign up on the waitlist on https://private.id to obtain your apiKey.



  - Python >= 3.6



  Licensing Restriction: This SDK is not licensed for use without the consent of the Private Identity.



  Installation of the CryptoNets Python SDK and its dependencies is provided by `pip`.
  If pip is not installed, see the [pip user guide](https://pip.pypa.io/en/stable/installing/ "pip User Guide") to install
  pip.

  To ensure smooth installation, it’s recommended to use:

  - `pip: 9.0.2` or greater

  The safest way to install the SDK is to use pip in a virtualenv:

  ```
  python -m pip install cryptonets_python_sdk
  ```

  If you have the cryptonets_python_sdk package installed and want to upgrade to the latest version, you can run:

  ```
  python -m pip install --upgrade cryptonets_python_sdk
  ```

  This will install the cryptonets_python_sdk package as well as all dependencies.
  See the [installation](https://privid-sdk.s3.us-east-2.amazonaws.com/cryptonets-python-sdk/installation.html) section of
  the SDK homepage Guide for more information.



  You will receive the Server URL and apiKey when you subscribe.



  `from cryptonets_python_sdk.factor import FaceFactor`

  `from cryptonets_python_sdk.settings.loggingLevel import LoggingLevel`



  ```
  server_url = URL of the server

  api_key = APIKEY issued on successful signup

  server = FaceFactor(url=server_url, api_key=api_key,
          logging_level=LoggingLevel.full)
  ```



  [Private Identity License](https://github.com/openinfer/PrivateIdentity/blob/e19cb4870048f14e04a6be99d3cab78f4d8c6360/images/AWS%20EULA%20Template%20(2020.11.20)%20(Private%20Identity).pdf)
  Copyright (c) 2020-present, Private Identity All rights reserved.



  [Getting Started](https://privid-sdk.s3.us-east-2.amazonaws.com/cryptonets-python-sdk/index.html#getting-started)

  [Installation](https://privid-sdk.s3.us-east-2.amazonaws.com/cryptonets-python-sdk/installation.html)

  [Usage](https://privid-sdk.s3.us-east-2.amazonaws.com/cryptonets-python-sdk/usage.html)

  [SDK-Docs](https://privid-sdk.s3.us-east-2.amazonaws.com/cryptonets-python-sdk/Factor/Face.html#cryptonets_python_sdk.factor.FaceFactor)


pkgId: 01830c25cb58dd68664d76a9affdb1a4768e428c5ce27bf3b2f16eaa6f36e6d2
name: python-cryptonice
arch: src
version: 1.4.2.3
epoch: 0
release: 1
summary: Perform TLS scan of a domain
url: https://github.com/F5-Labs/cryptonice
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptonice-1.4.2.3-1.src.rpm
checksum_type: sha256
description:
  _Cryptonice_ collects data on a given domain and performs a series of tests to check TLS configuration and supporting protocols such as HTTP2 and DNS. It makes heavy use of open source code and libraries, including the [**SSLyze**](https://github.com/nabla-c0d3/sslyze) library for TLS testing, [**JARM**](https://github.com/salesforce/jarm) code for TLS fingerprinting and [**Wappalyzer**](https://github.com/AliasIO/Wappalyzer) code for server components checks.

  Cryptonice is currently supported under Python 3.8 and later on the following platforms:

  - **Windows**
  - **Mac OS**
  - **Ubuntu 20.04**
  - **CentOS 8** (other Linux distros coming soon).



  A Linux compatible **Docker** container is also now available for those that prefer not to rely on installations of Python or any dependencies. With Docker already installed, first pull down the image:

  `docker pull f5labs/cryptonice`

  To run from the container, use the following command:

  `docker run --rm -it f5labs/cryptonice www.f5.com`

  If you want to output the JSON results to a folder on your local machine you must tell Docker to map a local path to a path within the container. The following example maps a folder in C:\Scratch to a new folder called /results within the container)::

  `docker run --rm -it --volume //c/scratch:/results f5labs/cryptonice www.f5.com --json_out --json_path /results`

  Once the scan is complete you should find the resulting www.f5.com.json file in your C:\Scratch folder.



  For detailed and up to date documentation, check out our ReadTheDocs pages:

  https://cryptonice.readthedocs.io/




  The easiest way to get started is by using PIP to install Cryptonice:

  `pip install cryptonice`

  [![asciicast](https://asciinema.org/a/354489.svg)](https://asciinema.org/a/354489?speed=3&autoplay=1)




  _cryptonice_ requires a domain name (such as www.github.com).

  `cryptonice www.github.com`

  Providing only a domain name and no other command line input will result in the following default dictionary of commands being run.

      {
  	    "id": "default",
  	    "port": 443,
  	    "scans": ["TLS", "HTTP", "HTTP2", "DNS", "JARM"],
  	    "tls_params": ["certificate_information", "ssl_2_0_cipher_suites", "ssl_3_0_cipher_suites","tls_1_0_cipher_suites", "tls_1_1_cipher_suites", "tls_1_2_cipher_suites","tls_1_3_cipher_suites", "http_headers"],
  	    "http_body": false,
  	    "force_redirect": true,
  	    "print_out": true,
  	    "generate_json": true,
  	    "targets": ["www.github.com"]
      }

  The user can also choose to specify custom commands. Each custom command must be preceded with the name of the option (ex: to specify the scans TLS and HTTP to run, the user must add _--scans TLS HTTP_ to the command line parameters)
  - _--PORT_: port to perform the scan on (default = 443)
  - _--SCANS_: scans to perform
      - _TLS_ scan, _HTTP_ headers, _HTTP2_ check, _DNS_ data
  - _--TLS_PARAMETERS_: TLS specific scans to perform (should be listed as specified below, with no commas between options):
      - all, no_vuln_tests, certificate_info, ssl_2_0_cipher_suites, ssl_3_0_cipher_suites, tls_1_0_cipher_suites,
        tls_1_1_cipher_suites, tls_1_2_cipher_suites, tls_1_3_cipher_suites, tls_compression,
        tls_1_3_early_data, openssl_ccs_injection, heartbleed, robot, tls_fallback_scsv,
        session_renegotiation, session_resumption, session_resumption_rate, http_headers
      - **all** results in all commands being run, **no_vuln_tests** results in certificate_info, http_headers and the cipher_suites commands being run.
      - More information on each of these scan options can be found at: https://nabla-c0d3.github.io/sslyze/documentation/available-scan-commands.html
  - _--HTTP_BODY_: Y/y or N/n - sets a Boolean variable to include or exclude HTTP pages information
  - _--FORCE_REDIRECTS_: Y/y or N/n - sets a Boolean variable to check for automatic redirects from port 80 to 443 in a TLS scan (default = Y)
  - _--PRINT_OUT_: Y/y or N/n - sets a Boolean variable to print scan results to console (default = Y)
  - _--JSON_OUT_: Y/y or N/n - sets a Boolean variable to print scan results to JSON output file (default = Y)

  [![asciicast](https://asciinema.org/a/354498.svg)](https://asciinema.org/a/354498)


  _cryptonice_ generates a JSON output file with the information requested by the input parameters. Output files will be named after the domain name and port provided (ex: target = www.github.com, port = 443, output = www.github.com-443.json)



  _cryptonice_ can be used within other projects as well. An example of this functionality can be found in the simple sample_script.py file. In that short script, the program input is a JSON file (sample_scan.json also provided) with the required commands. The data is read into a dictionary and sent to the scanner_driver function in cryptonice/scanner. Individual modules can also be called from outside functions, and will return a dictionary of the results. Further information on function parameters can be found in the code comments for each function.



  This code does not currently have the capability to scan a server based on an IP address and an SNI. Instead, the user must supply a hostname and internally the code will do a DNS resolution. This may lead to discrepancies in the IP address scanned in the TLS portions and the HTTP headers section. Certain domain names may also result in only one certificate being returned. The issue currently persists in the sslyze API where we get the certificate information, and we are working to find a solution.


pkgId: ea9d0f76a7e596ebf1b8a679dc2f89bec30c8b490400ee524f54eb835b53f60d
name: python-cryptoowl
arch: src
version: 0.0.73
epoch: 0
release: 1
summary: A library, that stores commonly used code for different modules in the CryptoOwl application
url: https://pypi.org/project/cryptoowl/
rpm_license: None
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptoowl-0.0.73-1.src.rpm
checksum_type: sha256
description:
  A library, that stores commonly used code for different modules in the CryptoOwl application.
  Never store username-password in this repository.


  [Link-1](https://medium.com/@joel.barmettler/how-to-upload-your-python-package-to-pypi-65edc5fe9c56)
  [Link-2](https://www.section.io/engineering-education/setting-up-cicd-for-python-packages-using-github-actions/)


pkgId: 69e3a9882f1d91d4226e52d2a2c445c9b8d0f341e0e25540703b1a52f9bddbff
name: python-cryptos
arch: src
version: 2.0.9
epoch: 0
release: 1
summary: Python Crypto Coin Tools
url: http://github.com/primal100/pybitcointools
rpm_license: MIT License
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptos-2.0.9-1.src.rpm
checksum_type: sha256
description:
  This is a fork of Vitalik Buterin's original [pybitcointools](https://github.com/vbuterin/pybitcointools) library.

  After a lot of work, the library is finally active again and being actively updated. This took a lot of work and unfortunately some backward imcompatible changes may have been introduced in v2. If you run into issues after upgrading open an issue and will try to help. If it's reasonable to do so we can consider restoring the previous behaviour in v2, otherwise will assist with migration.

  Installation:

  ```bash
  pip install cryptos
  ```

  Library now supports making and pushing raw transactions for:

  * Bitcoin mainnet
  * Bitcoin testnet
  * Bitcoin Cash mainnet
  * Bitcoin Cash testnet
  * Litecoin mainnet
  * Litecoin testnet
  * Dash mainnet
  * Dash testnet
  * Dogecoin mainnet
  * Dogecoin testnet

  Transaction broadcast has been tested for all of these.

  Segregated Witness transactions also supported for:
  * Bitcoin mainnet
  * Bitcoin testnet
  * Litecoin mainnet
  * Litecoin testnet

  Here are the first mainnet segwit transactions made with this library:

  Bitcoin: https://blockchain.info/tx/9f3bd4fa14e424abd5623ba98877e33cfee3e7bd6f9f71d7a39e402501458c81

  Litecoin: https://live.blockcypher.com/ltc/tx/b16ad0332ca3114f0dc773fda643c49e41308df4204940539bea5806cfee0989/
  https://live.blockcypher.com/ltc/tx/3b936180daf05adcd7e9f04b60e1ba9a4a6db486c0ad91cb795b29ca46313000/

  Aim is to provide a simple, class-based API which makes switching between different coins and mainnet and testnet, and adding new coins, all very easy.

  Roadmap:
  * Change from unittest to pytest
  * Extend wallets to make transactions
  * Read the docs page
  * E-commerce tools (exchange rates, short-time invoices)
  * Command client for easy creation, signing and broadcasting of raw transactions
  * Multi-crypto wallet CLI



  * Methods have a simple interface, inputting and outputting in standard formats
  * Classes for different coins with a common sync and async interface
  * Many functions can be taken out and used individually
  * Supports binary, hex and base58
  * Transaction deserialization format almost compatible with BitcoinJS
  * Electrum and BIP0032 support
  * Make and publish a transaction all in a single command line instruction with full control
  * Includes non-bitcoin-specific conversion and JSON utilities



  * Not a full node, has no idea what blocks are



  WARNING: While it's fun to mess around with this on the testnet, do not do the following procedure on the mainnet unless you really know what you are doing. Any value in the inputs not included in the outputs will be lost.
  So if the total inputs value is 1 BTC, and the total outputs amount to 0.6 BTC, 0.4 BTC will be given to the miners as a fee. The faster way, listed later in the README, ensures the difference between
  inputs and outputs is sent as change back to the sender (except for a small minter fee).
  If in doubt, before broadcasting a transaction, visit https://live.blockcypher.com/btc/decodetx/ and decode the raw tx
  and make sure it looks right. If you aren't familiar with how Bitcoin transactions work, you should run through
   this procedure a few times on the testnet before developing for mainnet.

  OTHER WARNING: If transactions are taking a long time to be confirmed, try increasing the fee from what the
  library calculates to use.


      > from cryptos import *
      > c = Bitcoin(testnet=True)
      > priv = sha256('a big long brainwallet password')
      > priv
      '89d8d898b95addf569b458fbbd25620e9c9b19c9f730d5d60102abbabcb72678'
      > pub = c.privtopub(priv)
      > pub
      '041f763d81010db8ba3026fef4ac3dc1ad7ccc2543148041c61a29e883ee4499dc724ab2737afd66e4aacdc0e4f48550cd783c1a73edb3dbd0750e1bd0cb03764f'
      > addr = c.pubtoaddr(pub)
      > addr
      'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1'
      > inputs = c.unspent(addr)
      > inputs
      [{'height': 0, 'tx_hash': '6d7a1b133f5ad2ce77d8980a1c84d7b595e4085d5a4a6d347e8a92df6ffc31f5', 'tx_pos': 0, 'value': 7495, 'address': 'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1'}, {'height': 0, 'tx_hash': 'e1e7b62e5eb4d399c75649e9256a91f0371268ca265ab9265a433bb263baf2f2', 'tx_pos': 0, 'value': 1866771, 'address': 'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1'}]
      > outs = [{'value': 1000000, 'address': 'tb1q95cgql39zvtc57g4vn8ytzmlvtt43skngdq0ue'}, {'value': sum(i['value'] for i in inputs) - 1000000 - 750 , 'address': 'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1'}]
      outs
      [{'value': 1000000, 'address': 'tb1q95cgql39zvtc57g4vn8ytzmlvtt43skngdq0ue'}, {'value': 873516, 'address': 'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1'}]
      > tx = c.mktx(inputs,outs)
      > tx
      {'locktime': 0, 'version': 1, 'ins': [{'height': 0, 'tx_hash': '6d7a1b133f5ad2ce77d8980a1c84d7b595e4085d5a4a6d347e8a92df6ffc31f5', 'tx_pos': 0, 'value': 7495, 'address': 'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1', 'script': '', 'sequence': 4294967295}, {'height': 0, 'tx_hash': 'e1e7b62e5eb4d399c75649e9256a91f0371268ca265ab9265a433bb263baf2f2', 'tx_pos': 0, 'value': 1866771, 'address': 'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1', 'script': '', 'sequence': 4294967295}], 'outs': [{'value': 1000000, 'script': '00142d30807e2513178a791564ce458b7f62d758c2d3'}, {'value': 873516, 'script': '76a914ad25bdf0fdfd21ca91a82449538dce47f8dc213d88ac'}]}
      > tx2 = c.signall(tx, priv)
      > tx2
      {'locktime': 0, 'version': 1, 'ins': [{'height': 0, 'tx_hash': '6d7a1b133f5ad2ce77d8980a1c84d7b595e4085d5a4a6d347e8a92df6ffc31f5', 'tx_pos': 0, 'value': 7495, 'address': 'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1', 'script': '473044022012ba62de78427811650f868209572404a0846bf60b3a3705799877bb5351827702202bcadc067f5dce01ecf10306e033a905a156aec71d769bcffc0e221a0c91c6030141041f763d81010db8ba3026fef4ac3dc1ad7ccc2543148041c61a29e883ee4499dc724ab2737afd66e4aacdc0e4f48550cd783c1a73edb3dbd0750e1bd0cb03764f', 'sequence': 4294967295}, {'height': 0, 'tx_hash': 'e1e7b62e5eb4d399c75649e9256a91f0371268ca265ab9265a433bb263baf2f2', 'tx_pos': 0, 'value': 1866771, 'address': 'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1', 'script': '47304402205c9b724d2499f167b9557b8efd13b8b2109ae287b712f2db1d3d46cfc31c71a702201a74bda43116977c4605d499177152afd3965b2fe586f3236053786ef19e96090141041f763d81010db8ba3026fef4ac3dc1ad7ccc2543148041c61a29e883ee4499dc724ab2737afd66e4aacdc0e4f48550cd783c1a73edb3dbd0750e1bd0cb03764f', 'sequence': 4294967295}], 'outs': [{'value': 1000000, 'script': '00142d30807e2513178a791564ce458b7f62d758c2d3'}, {'value': 873516, 'script': '76a914ad25bdf0fdfd21ca91a82449538dce47f8dc213d88ac'}]}
      > tx3 = serialize(tx2)
      > tx3
      '0100000002f531fc6fdf928a7e346d4a5a5d08e495b5d7841c0a98d877ced25a3f131b7a6d000000008a473044022012ba62de78427811650f868209572404a0846bf60b3a3705799877bb5351827702202bcadc067f5dce01ecf10306e033a905a156aec71d769bcffc0e221a0c91c6030141041f763d81010db8ba3026fef4ac3dc1ad7ccc2543148041c61a29e883ee4499dc724ab2737afd66e4aacdc0e4f48550cd783c1a73edb3dbd0750e1bd0cb03764ffffffffff2f2ba63b23b435a26b95a26ca681237f0916a25e94956c799d3b45e2eb6e7e1000000008a47304402205c9b724d2499f167b9557b8efd13b8b2109ae287b712f2db1d3d46cfc31c71a702201a74bda43116977c4605d499177152afd3965b2fe586f3236053786ef19e96090141041f763d81010db8ba3026fef4ac3dc1ad7ccc2543148041c61a29e883ee4499dc724ab2737afd66e4aacdc0e4f48550cd783c1a73edb3dbd0750e1bd0cb03764fffffffff0240420f00000000001600142d30807e2513178a791564ce458b7f62d758c2d32c540d00000000001976a914ad25bdf0fdfd21ca91a82449538dce47f8dc213d88ac00000000'
      > c.pushtx(tx3)
      'd5b5b148285da8ddf9d719627c21f5cbbb3e17ae315dbb406301b9ac9c5621e5'



  To send 0.00006 BTC from native segwit addr tb1qsp907fjefnpkczkgn62cjk4ehhgv2s805z0dkv belonging to privkey 89d8d898b95addf569b458fbbd25620e9c9b19c9f730d5d60102abbabcb72678
  to address tb1q95cgql39zvtc57g4vn8ytzmlvtt43skngdq0ue, with change returned to the sender address:

      > from cryptos import *
      > c = Bitcoin(testnet=True)
      > c.send("89d8d898b95addf569b458fbbd25620e9c9b19c9f730d5d60102abbabcb72678", "tb1qsp907fjefnpkczkgn62cjk4ehhgv2s805z0dkv", "tb1q95cgql39zvtc57g4vn8ytzmlvtt43skngdq0ue", 5000)
      72caf37e96081374284356aa20cffef31f4c1f158b87f46f7f995bfdaaf5d1c6'

  Or if you prefer to verify the tx (for example, at https://live.blockcypher.com/btc/decodetx/) you can break it into two steps:

      > from cryptos import *
      > c = Bitcoin(testnet=True)
      > tx = c.preparesignedtx("89d8d898b95addf569b458fbbd25620e9c9b19c9f730d5d60102abbabcb72678", "tb1qsp907fjefnpkczkgn62cjk4ehhgv2s805z0dkv", "tb1q95cgql39zvtc57g4vn8ytzmlvtt43skngdq0ue", 5000)
      > serialize(tx)
      '010000000001014ae7e7fdead71cc8303c5b5e68906ecbf978fb9d84f5f9dd505823356b9a1d6e0000000000ffffffff0288130000000000001600142d30807e2513178a791564ce458b7f62d758c2d3a00f000000000000160014804aff26594cc36c0ac89e95895ab9bdd0c540ef02483045022100cff4e64a4dd0f24c1f382e4b949ae852a89a5f311de50cd13d3801da3669675c0220779ebcb542dc80dae3e16202c0b58dd60261662f3b3a558dca6ddf22e30ca4230121031f763d81010db8ba3026fef4ac3dc1ad7ccc2543148041c61a29e883ee4499dc00000000'
      > c.pushtx(tx)
      '6a7c437b0de10c42f2e9e18cc004e87712dbda73a851a0ea6774749a290c7e7d'

  Another example with Litecoin testnet and a change address:

      > from cryptos import *
      > l = Litecoin(testnet=True)
      > tx = l.preparesignedtx("89d8d898b95addf569b458fbbd25620e9c9b19c9f730d5d60102abbabcb72678", "tltc1qsp907fjefnpkczkgn62cjk4ehhgv2s80d2dnx9", "tltc1q95cgql39zvtc57g4vn8ytzmlvtt43skn39z3vs", 967916800, change_addr="tltc1qst3pkm860tjt9y70ugnaluqyqnfa7h54q7xv2n")
      > tx
      '010000000144ea7b41df09cee54c43f817dc11fd4d88c9b721b4c13b588f6a764eab78f692000000008b4830450221008efa819db89f714dbe0a19a7eb605d03259f4755a0f12876e9dddf477e1867b8022072bc76d120e92668f4765b5d694aee4a3cafd6cd4aaa8d5ebf88c3f821c81d9c4141041f763d81010db8ba3026fef4ac3dc1ad7ccc2543148041c61a29e883ee4499dc724ab2737afd66e4aacdc0e4f48550cd783c1a73edb3dbd0750e1bd0cb03764fffffffff02003db139000000001976a91409fed3e08e624b23dbbacc77f7b2a39998351a6888acf046df07000000001976a914ad25bdf0fdfd21ca91a82449538dce47f8dc213d88ac00000000'
      > crypto.pushtx(tx)
      {'status': 'success', 'data': {'txid': 'd8b130183824d0001d3bc669b31e798e2654868a7fda743aaf35d757d89db0eb', 'network': 'tbcc'}}



      > from cryptos import *
      > coin = Bitcoin(testnet=True)
      > publickeys = ['02e5c473c051dae31043c335266d0ef89c1daab2f34d885cc7706b267f3269c609', '0391ed6bf1e0842997938ea2706480a7085b8bb253268fd12ea83a68509602b6e0', '0415991434e628402bebcbaa3261864309d2c6fd10c850462b9ef0258832822d35aa26e62e629d2337e3716784ca6c727c73e9600436ded7417d957318dc7a41eb']
      > script, address = coin.mk_multsig_address(publickeys, 2)
      > script
      '522102e5c473c051dae31043c335266d0ef89c1daab2f34d885cc7706b267f3269c609210391ed6bf1e0842997938ea2706480a7085b8bb253268fd12ea83a68509602b6e0410415991434e628402bebcbaa3261864309d2c6fd10c850462b9ef0258832822d35aa26e62e629d2337e3716784ca6c727c73e9600436ded7417d957318dc7a41eb53ae'
      > address
      '2ND6ptW19yaFEmBa5LtEDzjKc2rSsYyUvqA'
      > tx = coin.preparetx(address, "myLktRdRh3dkK3gnShNj5tZsig6J1oaaJW", 1100000, 50000)
      > for i in range(0, len(tx['ins'])):
          sig1 = coin.multisign(tx, i, script, "cUdNKzomacP2631fa5Q4yHv2fADc8Ueymr5Z5NUSJjVM13igcVJk")
          sig3 = coin.multisign(tx, i, script, "cMrziExc6iMV8vvAML8QX9hGDP8zNhcsKbdS9BqrRa1b4mhKvK6f")
          tx = apply_multisignatures(tx, i, script, sig1, sig3)
      > tx
      '0100000001e62c0b5434108607f52856bfbcf5093363fbd4789141a661a4c6c8042769ed2001000000fd1d0100483045022100dfc75916f6bb5c5b72a45dea44dbc45b47ba90912efb84680a373acadb3b1212022022dbbd66e4871624609d875bdb592d11335eb4ec49c7b87bb0b8bc76f72f80f30147304402204c38cab196ec0e82a9f65ecba70a0dbf73f49e5886e1000b9bc52894e28fa5c9022007bff3f90bcece19036625806d4d1951a03c256627163f1ac4e76a6ee8eae072014c89522102e5c473c051dae31043c335266d0ef89c1daab2f34d885cc7706b267f3269c609210391ed6bf1e0842997938ea2706480a7085b8bb253268fd12ea83a68509602b6e0410415991434e628402bebcbaa3261864309d2c6fd10c850462b9ef0258832822d35aa26e62e629d2337e3716784ca6c727c73e9600436ded7417d957318dc7a41eb53aeffffffff02e0c81000000000001976a914c384950342cb6f8df55175b48586838b03130fad88ac301224030000000017a914d9cbe7c2c507c306f4872cf965cbb4fe51b621998700000000'
      > coin.pushtx(tx)
      {'status': 'success', 'data': {'txid': 'b64e19311e3aa197063e03657679e2974e04c02c5b651c4e8d55f428490ab75f', 'network': 'BTCTEST'}}




  There is also an asyncio interface for all methods requiring interaction with the explorer:

      python -m asyncio
      > from cryptos import *
      > c = Bitcoin(testnet=True)
      > await c.send("89d8d898b95addf569b458fbbd25620e9c9b19c9f730d5d60102abbabcb72678", "tb1qsp907fjefnpkczkgn62cjk4ehhgv2s805z0dkv", "tb1q95cgql39zvtc57g4vn8ytzmlvtt43skngdq0ue", 5000)
      72caf37e96081374284356aa20cffef31f4c1f158b87f46f7f995bfdaaf5d1c6'



      > from cryptos import *
      > priv = sha256('a big long brainwallet password')
      > b = Bitcoin()
      > b.privtoaddr(priv)
      '1GnX7YYimkWPzkPoHYqbJ4waxG6MN2cdSg'
      > b = Bitcoin(testnet=True)
      > b.privtoaddr(priv)
      'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1'
      > l = Litecoin()
      > l.privtoaddr(priv)
      'Lb1UNkrYrQkTFZ5xTgpta61MAUTdUq7iJ1'
      > l = Litecoin(testnet=True)
      > l.privtoaddr(priv)
      'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1'
      > c = BitcoinCash()
      > c.privtoaddr(priv)
      '1GnX7YYimkWPzkPoHYqbJ4waxG6MN2cdSg'
      > c = BitcoinCash(testnet=True)
      > c.privtoaddr(priv)
      'mwJUQbdhamwemrsR17oy7z9upFh4JtNxm1'
      > d = Dash()
      > d.privtoaddr(priv)
      'XrUMwoCcjTiz9gzP9S9p9bdNnbg3MvAB1F'
      > d = Dash(testnet=True)
      > d.privtoaddr(priv)
      'yc6xxkH4B1P4VRuviHUDBd3j4tAQpy4fzn'
      > d = Doge()
      > d.privtoaddr(priv)
      'DLvceoVN5AQgXkaQ28q9qq7BqPpefFRp4E'




  Aims to be compatible with https://iancoleman.io/bip39/. Good choice for supporting different coins and networks from
  the same wallet words. Also compatible with electrum when bip39 option is selected.

      > from cryptos import *
      > words = entropy_to_words(os.urandom(16))
      > words
      'practice display use aisle armor salon glue okay sphere rather belt mansion'
      > keystore.bip39_is_checksum_valid(words)
      (True, True)
      > coin = Bitcoin()
      > wallet = coin.wallet(words)
      > wallet.keystore.root_derivation
      "m/44'/0'/0'"
      > wallet.keystore.xprv      #Account Extended Private Key
      'xprv9y1M42LhHxTuoQbCuySz4Ek6EB3guE4CiXDXhHQnR7LwgpUV7AxQVm7D4HpUWRStwUXFepQRz7av2iaAXXYoizT9JoqWE6qffxNdiMxFQtc'
      > wallet.keystore.xpub      # Account Extended Public Key
      'xpub6BzhTXsb8L2D1tfg1zyzRNgpnCtBJgn45k98VfpPySsvZcodeiGf3ZRguZLoS6VwEQ4iZ7Y4bq5A5eqyooyc4jC9beFTB3mmxrGwjRLa3pm'
      > addr1 = wallet.new_receiving_address()
      > addr1
      '18q3EiCiKd5vydnaVwWEpAFyzfL2ftAZ1L'
      > wallet.privkey(addr1)
      'L4cKz3epcM3CAmwkSwJwZ2c4q5ukmSVWCrE9PqE46ybU3XyzfTYx'
      > addr2 = wallet.new_change_address()
      > addr2
      '1BgkpwEDrTbCNduyR97EpW4zvFhEzWsyvi'
      > wallet.privkey(addr2)
      'L5Xmbnsen2cN36WxbuHkAixBmuJ8b3GZPmLtaRPf66p4gfnqHDqi'
      > addr3 = wallet.new_change_address()
      > addr3
      '1KjxFsDP9SXAmKKD4ZgSep5kYaYgAGK3P9'
      > priv3 = wallet.privkey(addr3)
      > priv3
      'L1ktR1kifTXLXoroLZiB3AF9UtKLRW2FmYnnR7VbPZBYkscgRkyn'
      > assert coin.privtoaddr(priv3) == addr3
      True

  Dash example:

      > from cryptos import *
      > words = 'practice display use aisle armor salon glue okay sphere rather belt mansion'
      > coin = Dash()
      > wallet = coin.wallet(words)
      > wallet.keystore.root_derivation
      "m/44'/5'/0'"
      > wallet.keystore.xprv      #Account Extended Private Key
      'xprv9yiTHjM4MPNQndsxkrvE2QgF36nvutGt3e9k5DjkjfAnNbqGm1wL77XV2xHiwnUfwcgAZUWkdpEnxRWELTrgXDVhvntNFwme1CqCgm1a91f'
      > wallet.keystore.xpub      # Account Extended Public Key
      'xpub6ChohEsxBkvi17xRrtTEPYcyb8dRKLzjQs5Lsc9NHzhmFQARJZFaeuqxtEMHaF4J8MzatWSYrmq2qAc3BaxFiKzEwX1AKQx5uWHZr3y8s82'
      > addr1 = wallet.new_receiving_address()
      > addr1
      'Xea1GEenz6Toq5YQjvjz86MTT8ezT5ZwnY'
      > wallet.privkey(addr1)
      'XDbSZeVzBiHanwrSU5yripFd8Lq5tnrjxgvbaksNPhAExbS29aAa'
      > addr2 = wallet.new_change_address()
      > addr2
      'XwYCR4CwafwoGe6P4H9LndaqAQkmE6xYix'
      > wallet.privkey(addr2)
      'XHwHKxVfhzPEGZGGfQ9uwKK2xQjavF2yNUkq7FGXFA6SyZv4jge1'
      > addr3 = wallet.new_change_address()
      > addr3
      'XfZwJaFiBx4qLqnQydvqGyWDPciAtjFmgn'
      > priv3 = wallet.privkey(addr3)
      > priv3
      'XCNac8eQE642wWKaxnWHLa1GW1Y1uppvT5uda3LYVXAJZAAdR1Fx'
      > assert coin.privtoaddr(priv3) == addr3
      True



      > from cryptos import *
      > words = entropy_to_words(os.urandom(20))
      > words
      'jealous silver churn hedgehog border physical market parent hungry design cage lab drill clay attack'
      > keystore.bip39_is_checksum_valid(words)
      (True, True)
      > coin = Bitcoin()
      > wallet = coin.p2wpkh_p2sh_wallet(words)
      > wallet.keystore.root_derivation
      "m/49'/0'/0'"
      > wallet.keystore.xprv      #Account Extended Private Key
      'yprvAHoU8z6164hTNdwpArPgn2bdNExmUu9HwxeyhUok8pLDNQSCzYo8rvD6tFvMKk4EQXF2UGzRea5FBHjrtcuYmuBB7Z6EoznKCPeUwXaZduB'
      > wallet.keystore.xpub      # Account Extended Public Key
      'ypub6WnpYVctvSFkb82HGsvh9AYMvGoFtMs9KBaaVsDMh9sCFCmMY67PQiXajW1FQq7AKsgvWGSrmZ82rquUpwcKR6Ey1sdMdeQWvgCKvABjWy8'
      > addr1 = wallet.new_receiving_address()
      > addr1
      '38yA1L6u6NiADrafrqZKDt1fTRHpGC3E7g'
      > wallet.privkey(addr1)
      'Ky13njnYGrj5jowjUarqcmaRCG37zSwqRJkTj296cQsSvFtsV5a5'
      > addr2 = wallet.new_change_address()
      > addr2
      '3B5f8vVBRTAh2krbd4PiCtpyn7LhFJBDdV'
      > wallet.privkey(addr2)
      'KzCNhiuvwQ1T6hXL21Act86HacauJGe1c8ttECqx1Fai6tPc1bEG'
      > addr3 = wallet.new_change_address()
      > addr3
      '3NvrTctHm6dQc6G2p3XYciWH8H6Lfcz9Jc'
      > wallet.privkey(addr3)
      'KwdZhDopz3UVNW3Qso5UiyGkiDmayRZmAZdfAojvGsoP7da7HueX'



      > from cryptos import *
      > words = 'jealous silver churn hedgehog border physical market parent hungry design cage lab drill clay attack'
      > coin = Bitcoin()
      > wallet = coin.p2wpkh_wallet(words)
      > wallet.keystore.root_derivation
      "m/84'/0'/0'
      > wallet.keystore.xprv      #Account Extended Private Key
      'zprvAcSKXVdgJHh5vyEeC6HSVScUCHxrKEWkkFSE2YsLpTborr4y2rHMrmr66yvxkGVqiiwwUCqUVkPB7o5ThnK3Dybi5PEywikXbNKQcHNMYPd'
      > wallet.keystore.xpub      # Account Extended Public Key
      'zpub6qRfw1Aa8fFP9TK7J7pSraZCkKoLihEc7UMppwGxNo8njeQ7aPbcQaAZxFtnjCj9XveSJEnwV88YPyXXUCr3yRSSAKzibVCQB7AudUQn6Qg'
      > addr1 = wallet.new_receiving_address()
      > addr1
      'bc1qkh6wwkyhfceuxq236pc9gtv2agfqnelzh5m94f'
      > wallet.privkey(addr1)
      'Kwnaq7cvD4CAnTcppou6wpUpMFx5yZRqkpZcy6bBvPVKp2FQzJNf'
      > addr2 = wallet.new_change_address()
      > addr2
      'bc1qj3vc5ft8nuka447z7ecujksszq6cm2r8p750n9'
      > wallet.privkey(addr2)
      'L1QjmcLmeR5tbH62WxKoSdZBBHn69PuQSnLo2LaimnztsDANMP5M'
      > addr3 = wallet.new_change_address()
      > addr3
      'bc1qft00enx8c6unn00pmfdgq36ftd0u0q4lk5ajpy'
      > wallet.privkey(addr3)
      'Kx91EteCnRmUPr8eibiEsAcFDyKJ2z9uAwGUQfMVw5ABQx7QyVgg'


  These aim to be compatible with the default Electrum wallet seed style. They do not have different derivation paths for different coins.
  No checks have been made against any non-Bitcoin Electum wallet (e.g. Electrum Litecoin, Electron Cash)
  At this moment, there is no support generating the seed words Electrum requires (which contains versioning) so seed words need to be copied from Electrum.
  Electrum versioning allows for auto-detection of wallet type, .e.g standard or segwit.

      > from cryptos import *
      > seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
      > wallet = Bitcoin().electrum_wallet(seed_words)
      > wallet.keystore.xtype
      'p2wpkh'
      > wallet.keystore.root_derivation
      "m/0'/"
      > wallet.keystore.xprv
      'zprvAZswDvNeJeha8qZ8g7efN3FXYVJLaEUsE9TW6qXDEbVe74AZ75c2sZFZXPNFzxnhChDQ89oC8C5AjWwHmH1HeRKE1c4kKBQAmjUDdKDUZw2'
      > wallet.keystore.xpub
      'zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ'
      > addr1 = wallet.new_receiving_address()
      > addr1
      'bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af'
      > wallet.privkey(addr1)
      'L9fSXYNxYWHJWUqrQ6yhZCAJXq6XsfvcJ1Y2EnMAZfLLRNVQswQj'
      > addr2 = wallet.new_change_address()
      > addr2
      'bc1qdy94n2q5qcp0kg7v9yzwe6wvfkhnvyzje7nx2p'
      > wallet.privkey(addr2)
      'L8rPGyfyzdLLEzxuBeC87Jvpp8FKxwrRtmkZ2PkRmRjqxNF8TVwG'
      > addr3 = wallet.new_change_address()
      > addr3
      'bc1q6xwxcw6m9ga35687tnu5tstmsvmzjwdnzktemv'
      > wallet.privkey(addr3)
      'L7NeR6r9yU2n4zddxTCUpKYmzugYuouyLsCZR9naTqkBW6sjpxDM'



  For security reasons the seed and xprv should ideally be held in cold storage only. If a web application needs to be
  able to provide addresses on demand, the solution is to use a watch wallet, generated from the xpub.

  For example, let's take the Dash xpub from a previous example:

      > from cryptos import *
      > coin = Dash()
      > xpub = 'xpub6ChohEsxBkvi17xRrtTEPYcyb8dRKLzjQs5Lsc9NHzhmFQARJZFaeuqxtEMHaF4J8MzatWSYrmq2qAc3BaxFiKzEwX1AKQx5uWHZr3y8s82'
      > wallet = coin.watch_wallet(xpub)
      > wallet.is_watching_only
      True
      > wallet.new_receiving_address()
      'Xea1GEenz6Toq5YQjvjz86MTT8ezT5ZwnY'
      > wallet.new_change_address()
      'XwYCR4CwafwoGe6P4H9LndaqAQkmE6xYix'

  Full list of wallet methods:

  * wallet -> BIP 39 Standard
  * watch_wallet -> BIP 39 Standard, watch-only
  * p2wpkh_p2sh_wallet -> BIP 39 Segwit P2SH addresses, beginning with 3 for Bitcoin mainnet
  * watch_p2wpkh_p2sh_wallet -> BIP 39 Segwit P2SH addresses, watch-only
  * p2wpkh_wallet -> BIP 39 New Segwit Addresses, beginning with 'bc' for Bitcoin mainnet
  * watch_p2wpkh_wallet -> BIP New Segwit Address, watch-only
  * electrum_wallet -> detects p2kh or p2wpkh based on seed
  * watch_electrum_wallet -> Watch electrum standard wallet
  * watch_electrum_p2wpkh_wallet -> Watch electrum new segwit wallet


      > import os
      > from cryptos import *
      > words = entropy_to_words(os.urandom(16))
      > words
      'float skirt road remind fire antique vendor select senior latin small glide'
      > seed = mnemonic_to_seed(words)
      > seed
      b'\xb7Z\x9b\x9b\x9c\x1bq\x81\x1b\xdc\x98\x1c\xbc\xb8\xbb\x130\xea,\xda\x14\xeb\x9bF\xafu\x88\xc2\xf9\xfc\x7f\xd0\xb0?\x9d\xf3\xa7$0Tx\xd3\xb7\x82\x87U\xe7\xcc\xdd\x16\xddd\xbf'T\t_\xdc R!x\t'
      > electrum_privkey(seed, 0)
      '5a37812b3057e44636c6e07023e16a8669e12a4365dfabbcb376ed272081d522'
      > electrum_privkey(seed, 300, 0)
      '04cf414f200cd090239f2116d90608a74eae34ae21103ca9eef7bd9579e48bed'
      > electrum_privkey(seed, 0, 1)      #Change address
      '9ca3631f813a6f81b70fbfc4384122bfe6fb159e6f7aea2811fe968c2a39d42a'




  The following scripts are included. They also provide good sample code for using the library.
  Run each script with -h option for details on the cli options.

              broadcast: Broadcast a transaction from raw hex
              convert_private_key: Convert a private key to a new format
              create_private_key: Generate a new private key
              cryptosend: Send crypto to another address
              explorer: View information in the blockchain about a block, transaction or address
              get_block_sizes: Get the size of a series of blocks
              subscribe: Subscribe to an address for changes
              view_private_key_addresses: View details about a private key including available addresses



  Cryptool is back to work in progress. This is a legacy reference for now. Use the scripts from the previous section for now.


      cryptotool bip32_master_key 21456t243rhgtucyadh3wgyrcubw3grydfbng
      xprv9s21ZrQH143K2napkeoHT48gWmoJa89KCQj4nqLfdGybyWHP9Z8jvCGzuEDv4ihCyoed7RFPNbc9NxoSF7cAvH9AaNSvepUaeqbSpJZ4rbT

      cryptotool bip32_ckd xprv9s21ZrQH143K2napkeoHT48gWmoJa89KCQj4nqLfdGybyWHP9Z8jvCGzuEDv4ihCyoed7RFPNbc9NxoSF7cAvH9AaNSvepUaeqbSpJZ4rbT 0
      xprv9vfzYrpwo7QHFdtrcvsSCTrBESFPUf1g7NRvayy1QkEfUekpDKLfqvHjgypF5w3nAvnwPjtQUNkyywWNkLbiUS95khfHCzJXFkLEdwRepbw

      cryptotool bip32_privtopub xprv9s21ZrQH143K2napkeoHT48gWmoJa89KCQj4nqLfdGybyWHP9Z8jvCGzuEDv4ihCyoed7RFPNbc9NxoSF7cAvH9AaNSvepUaeqbSpJZ4rbT
      xpub661MyMwAqRbcFGfHrgLHpC5R4odnyasAZdefbDkHBcWarJcXh6SzTzbUkWuhnP142ZFdKdAJSuTSaiGDYjvm7bCLmA8DZqksYjJbYmcgrYF

  The -s option lets you read arguments from the command line

      cryptotool sha256 'some big long brainwallet password' | pybtctool -s privtoaddr | pybtctool -s history
      [{'output': u'97f7c7d8ac85e40c255f8a763b6cd9a68f3a94d2e93e8bfa08f977b92e55465e:0', 'value': 50000, 'address': u'1CQLd3bhw4EzaURHbKCwM5YZbUQfA4ReY6'}, {'output': u'4cc806bb04f730c445c60b3e0f4f44b54769a1c196ca37d8d4002135e4abd171:1', 'value': 50000, 'address': u'1CQLd3bhw4EzaURHbKCwM5YZbUQfA4ReY6'}]
      cryptotool random_electrum_seed | pybtctool -s electrum_privkey 0 0
      593240c2205e7b7b5d7c13393b7c9553497854b75c7470b76aeca50cd4a894d7

  The -b option lets you read binary data as an argument

      cryptotool sha256 123 | pybtctool -s changebase 16 256 | pybtctool -b changebase 256 16
      a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae30a

  The -j option lets you read json from the command line (-J to split a json list into multiple arguments)

      cryptotool unspent 1FxkfJQLJTXpW6QmxGT6oF43ZH959ns8Cq | pybtctool -j select 200000001 | pybtctool -j mksend 1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P:20000 1FxkfJQLJTXpW6QmxGT6oF43ZH959ns8Cq 1000 | pybtctool -s signall 805cd74ca322633372b9bfb857f3be41db0b8de43a3c44353b238c0acff9d523
      0100000003d5001aae8358ae98cb02c1b6f9859dc1ac3dbc1e9cc88632afeb7b7e3c510a49000000008b4830450221009e03bb6122437767e2ca785535824f4ed13d2ebbb9fa4f9becc6d6f4e1e217dc022064577353c08d8d974250143d920d3b963b463e43bbb90f3371060645c49266b90141048ef80f6bd6b073407a69299c2ba89de48adb59bb9689a5ab040befbbebcfbb15d01b006a6b825121a0d2c546c277acb60f0bd3203bd501b8d67c7dba91f27f47ffffffff1529d655dff6a0f6c9815ee835312fb3ca4df622fde21b6b9097666e9284087d010000008a473044022035dd67d18b575ebd339d05ca6ffa1d27d7549bd993aeaf430985795459fc139402201aaa162cc50181cee493870c9479b1148243a33923cb77be44a73ca554a4e5d60141048ef80f6bd6b073407a69299c2ba89de48adb59bb9689a5ab040befbbebcfbb15d01b006a6b825121a0d2c546c277acb60f0bd3203bd501b8d67c7dba91f27f47ffffffff23d5f9cf0a8c233b35443c3ae48d0bdb41bef357b8bfb972336322a34cd75c80010000008b483045022014daa5c5bbe9b3e5f2539a5cd8e22ce55bc84788f946c5b3643ecac85b4591a9022100a4062074a1df3fa0aea5ef67368d0b1f0eaac520bee6e417c682d83cd04330450141048ef80f6bd6b073407a69299c2ba89de48adb59bb9689a5ab040befbbebcfbb15d01b006a6b825121a0d2c546c277acb60f0bd3203bd501b8d67c7dba91f27f47ffffffff02204e0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac5b3c4411000000001976a914a41d15ae657ad3bfd0846771a34d7584c37d54a288ac00000000

  Fun stuff with json:

      cryptotool unspent 1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P | pybtctool -j multiaccess value | pybtctool -j sum
      625216206372

      cryptotool unspent 1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P | pybtctool -j count
      6198

  To use the testnet you can add --testnet:

      cryptotool unspent 2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF --testnet
      [{"output": "209e5caf8997a3caed4dce0399804ad7fa50c70f866bb7118a42c79de1b76efc:1", "value": 120000000}, {"output": "79f38b3e730eea0e44b5a2e645f0979
      2d9f8732a823079ba4778110657cbe7b2:0", "value": 100000000}, {"output": "99d88509d5f0e298bdb6883161c64c7f54444519ce28a0ef3d5942ff4ff7a924:0", "value
      ": 82211600}, {"output": "80acca12cf4b3b562b583f1dc7e43fff936e432a7ed4b16ac3cd10024820d027:0", "value": 192470000}, {"output": "3e5a3fa342c767d524b653aec51f3efe2122644c57340fbf5f79c75d1911ad35:0", "value": 10000000}]

  Or the --coin option to use a coin other than bitcoin (bch, btc, dash, doge or ltc)

      cryptotool unspent LV3VLesnCi3p3zf26Y86kH2FZxfQq2RjrA --coin ltc
      [{"output": "42bfe7376410696e260b2198f484f5df4aa6c744465940f9922ac9f8589670a4:0", "value": 14282660}]

      cryptotool unspent myLktRdRh3dkK3gnShNj5tZsig6J1oaaJW --coin ltc --testnet
      [{"output": "68f9c662503715a3baf29fe4b07c056b0bf6654dbdd9d5393f4d6a18225d0ff3:0", "value": 16333531}, {"output": "aa40041a1fcdb952d6a38594a27529f890d17d715fd54b6914cd6709fa94ca67:0", "value": 100000000}, {"output": "3b72bae956d27ab0ad309808ab76beaf203109f423e533fd7c40f1201672f598:1", "value": 164712303}]

  Make and broadcast a transaction on the Bitcoin Cash testnet:

      cryptotool send 89d8d898b95addf569b458fbbd25620e9c9b19c9f730d5d60102abbabcb72678 mgRoeWs2CeCEuqQmNfhJjnpX8YvtPACmCX 999950000 --fee 50000 --coin bch --testnet
      {"status": "success", "data": {"txid": "caae4c059ac07827047237560ff44f97c940600f8f0a1e3392f4bcaf91e38c5c", "network": "tbcc"}}

  The arguments are the private key of the sender, the receiver's address and the fee (default 10000). Change will be returned to the sender.



  * privtopub            : (privkey) -> pubkey
  * pubtoaddr            : (pubkey) -> address
  * privtoaddr           : (privkey) -> Convert a wif encoded private key to correct address, if not wif encoded then p2pkh
  * privtop2pkh          : (privkey) -> p2pkh address
  * encode_privkey       : (privkey, format, script_type="p2pkh") -> privkey
  * sign                 : (txobj, i, privkey) -> create digital signature of tx with privkey and add to input i
  * signall              : (txobj, privkey) -> create digital signature of tx with privkey for all inputs
  * history              : (address, merkle_proof=False) -> tx history of an address
  * get_histories        : (*addresses, merkle_proof=False) -> tx histories of an address
  * unspent              : (address, merkle_proof=False) -> unspent outputs for an addresses
  * get_unspents         : (addresses, merkle_proof=False) -> unspent outputs for multiple addresses
  * pushtx               : (hex or tx object) -> push a transaction to the blockchain
  * get_raw_tx           : (txid) -> Get the raw hex of a transaction id
  * get_tx               : (txid) -> fetch a tx from the blockchain
  * get_verbose_tx       : (txid) -> Transaction details verbose output
  * get_txs              : (*txids) -> Fetch multiple transaction details
  * calculate_fee        : (Tx) -> Calculate the fee of a transaction
  * send                 : (privkey, frm, to, value, change_addr=None, fee=None, estimate_fee_blocks: int = 6) -> create and a push a simple transaction to send coins to an address and return change to the change address or sender
  * send_to_multiple_receivers_tx          : (privkey, addr outs:value pairs, change_addr=None,fee=10000,, estimate_fee_blocks: int = 6) -> create and a push a transaction to send coins to multiple addresses and return change to the change address or sender
  * preparetx            : (frm, to, value, fee, estimate_fee_blocks: int = 6,change_addr=None): -> create unsigned txobj with change output
  * preparemultitx       : (frm, outs:value pairs, change_addr=None, fee=None, estimate_fee_blocks: int = 6): -> create unsigned txobj with multiple outputs and additional change output
  * preparesignedtx      : (privkey, frm, to, value, change_addr=None, fee=10000, estimate_fee_blocks: int = 6) -> create signed txobj with change output
  * preparesignedmultirecipienttx : (privkey, frm, outs: value pairs, change_addr=None, fee=10000, estimate_fee_blocks: int = 6) -> create signed txobj with multiple outputs and additional change output
  * mktx                 : (inputs, outputs, locktifme=0, sequence=0xFFFFFFFF) -> create unsigned txobj
  * mktx_with_change     : (inputs, outputs, change_addr=None, fee=None, estimate_fee_blocks=6, locktime=0, sequence=0xFFFFFFFF) -> create unsigned txobj
  * mk_multisig_address  : (pubkeys, M) -> Returns both M-of-N multisig script and address
  * multisign            : (txobj, i, script, privkey) -> signature
  * apply_multisignatures: (txobj, i, script, sigs) -> tx with index i signed with sigs
  * mk_multsig_segwit_address : (pubkeys, M)- Returns both M-of-N multisig script and native segwit address
  * mk_multsig_cash_address: (pubkeys, M)- Returns both M-of-N multisig script and Bitcoin Cash address
  * pubtop2w             : (pub) -> pay to witness script hash (segwit address)
  * privtop2w            : (priv) -> pay to witness script hash (segwit address)
  * pubtop2wpkh_p2sh     : (pub) -> Convert a public key to a segwit address encoded in a p2sh script
  * privtop2wpkh_p2sh    : (priv) -> Convert a private key to a segwit address encoded in a p2sh script
  * hash_to_segwit_addr  : (hash) -. Convert a public key hash to a native segwit address
  * scripthash_to_segwit_addr: (hash) -> Convert the hash of a p2sh script to a native segwit address
  * is_address           : (addr) -> true if addr is a valid address for this network
  * is_p2pkh              : (address) -> Return true if an address is in p2pkh format
  * is_p2sh              : (addr) -> true if addr is a pay to script hash for this network
  * is_segwit            : (priv, addr) -> true if priv-addr pair represent a pay to witness script hash
  * is_cash_or_legacy_p2pkh_address : (address) -> Returns true if an address is a legacy p2pkh address or a Bitcoin Cash formatted p2pkh address
  * is_native_segwit      : (address) -> Returns true if an address is of native segwit type
  * is_cash_address       : (address) -> Returns true if an address is of Bitcoin Cash address format
  * maybe_legacy_segwit   : (address) -> Returns true if address is likely a legacy segwit p2sh address
  * is_p2wsh              : (address) -> Returns true is address is a Pay To Witness Script Address
  * is_segwit_or_p2sh     : (address) -> Returns true if an address is a Pay to Witness or Pay to Script address
  * current_block_height : () -> Latest block height
  * block_height         : (txhash) -> Block height containing the txhash
  * inspect              : (tx_hex) -> Deserialize a transaction and decode and ins and outs
  * merkle_prove         : (txhash) -> Proves a transaction is valid and returns txhash, merkle siblings and block header.
  * estimate_fee_per_kb  : (numblocks) -> Get estimated fee kb to get transaction confirmed within numblocks number of blocks
  * tx_size              : (tx) -> Estimate final transaction size of an unsigned transaction
  * estimate_fee         : (tx, numblocks=6) -> Estimate required fee of an unsigned transaction
  * raw_block_header     : (height) -> Get the raw data of a block header
  * block_header         : (height) -> Get the decoded details of a block header
  * block_headers        : (*heights) -> Get a list of decoded block headers
  * subscribe_to_block_headers: (callback(height, raw_header, decoded_header)) -> Run a callback whenever a new block is added to the blockchain
  * unsubscribe_from_block_headers: () -> Remove all subscriptions to block headers
  * confirmations        : (height) -> Number of confirmations a transaction at this height has
  * subscribe_to_address : (callback(addr, status), addr) -> Run a callback every time there is an activity on an address
  * subscribe_to_address_transactions : (callback(address: str, txs, newly_confirmed, history, unspent, confirmed, unconfirmed, proven), addr) -> Run a callback every time there is an activity on an address with details transaction and balance information already retrieved
  * unsubscribe_from_address: (addr) -> Remove all subscriptions for this address
  * get_balance           : (addr) -> Get the balance, confirmed and unconfirmed, for an address
  * get_balances          : (*addrs) -> Get the balance, confirmed and unconfirmed, for multiple addresses
  * get_merkle            : (tx) -> Get the merkle root of a transaction
  * merkle_prove          : (tx) -> Prove a transaction is valid
  * merkle_prove_by_txid  : (txid) -> Prove a transaction id is valid
  * balance_merkle_proven : (addr) -> Get the merkle proven balance for an address
  * balance_merkle_proven : (*addrs) -> Get the merkle proven balance for multiple addresses
  * get_address_variations: (addr) -> Return alternative formats for an address (e.g. Standard + Bitcoin Cash address)
  * pub_is_for_p2pkh_addr : (pub, addr) -> Returns true if the p2pkh address for pub is addr
  * wiftoaddr             : (priv) -> Convert a private key in WIF format to the correct address type
  * electrum_address      : (masterkey, n, for_change) -> Electrum Address (for old Electrum seeds)
  * encode_privkey        : (privkey, format, script_type) -> Convert a private key to a different format or script types
  * output_script_to_address : (script) -> Convert an output script to an address
  * scripttoaddr          : (script)  -> Convert an input script to an address
  * addrtoscript          : (address) -> Convert an address to an output script
  * addrtoscripthash      : (address) -> Convert an address to scripthash as required by ElectrumX servers
  * p2sh_scriptaddr       : (script) -> Convert an output P2SH script to a P2SH address
  * p2sh_segwit_addr      : (script) -> Convert an output P2SH script to a native segwit P2WSH address
  * pubtosegwitaddress    : (pub) -> Convert a public key to a native segwit address
  * privtosegwitaddress   : (priv) -> Convert a private key to a native segwit address
  * scripthash_to_cash_addr: (scripthash) -> Convert a scripthash to a Bitcoin Cash address
  * pubtocashaddress      : (pub) -> Convert a public key to a Bitcoin Cash address
  * privtocashaddress     : (priv) -> Convert a private key to a Bitcoin Cash address
  * p2sh_cash_addr        : (script) -> Convert an script to a Bitcoin Cash address
  * hash_to_cash_addr     : (hash) -> Convert the hash of a p2sh script to a Bitcoin Cash address
  * legacy_addr_to_cash_address: (addr) -> Convert a legacy P2PKH address to a Bitcoin Cash address
  * cash_address_to_legacy_addr: (addr) -> Convert a Bitcoin Cash address to a legalcy P2PKH address



  * add                  : (key1, key2) -> key1 + key2 (works on privkeys or pubkeys)
  * multiply             : (pubkey, privkey) -> returns pubkey * privkey

  * ecdsa_sign           : (message, privkey) -> sig
  * ecdsa_verify         : (message, sig, pubkey) -> True/False
  * ecdsa_recover        : (message, sig) -> pubkey

  * random_key           : () -> privkey
  * random_electrum_seed : () -> electrum seed

  * electrum_stretch     : (seed) -> secret exponent
  * electrum_privkey     : (seed or secret exponent, i, type) -> privkey
  * electrum_mpk         : (seed or secret exponent) -> master public key
  * electrum_pubkey      : (seed or secexp or mpk) -> pubkey

  * bip32_master_key     : (seed) -> bip32 master key
  * bip32_ckd            : (private or public bip32 key, i) -> child key
  * bip32_privtopub      : (private bip32 key) -> public bip32 key
  * bip32_extract_key    : (private or public bip32_key) -> privkey or pubkey

  * deserialize          : (hex or bin transaction) -> JSON tx
  * serialize            : (JSON tx) -> hex or bin tx
  s* verify_tx_input      : (tx, i, script, sig, pub) -> True/False
  * tx_hash              : (hex or bin tx) -> hash

  * access               : (json list/object, prop) -> desired property of that json object
  * multiaccess          : (json list, prop) -> like access, but mapped across each list element
  * slice                : (json list, start, end) -> given slice of the list
  * count                : (json list) -> number of elements
  * sum                  : (json list) -> sum of all values

  * select               : (unspent, value) -> returns list of unspents which are enough to cover the value


  Another reminder, if you are doing something new with the library, whether a regular transaction, multisig,
  segwit or a coin you haven't worked with before, try it out in testnet first or alternatively with small amounts
  on the mainnet. The original pybitcointools had issues opened in Github where people lost money either due to
  not understanding what they were doing or because of bugs.




  If you wish to work together on crypto or other software related projects you can contact me using social links on my Github profile.
  I can very easily and quickly build software tools on top of this pybitcointools library.


pkgId: 1722771f80647dcfc91a3dc8779f75f72aee1c07e7014a798d61db3e1f34acf7
name: python-cryptpy
arch: src
version: 0.3.7
epoch: 0
release: 1
summary: Dynamically programmable Python/JS botnet (educational purposes only).
url: https://github.com/mitsukomegumi/CryptPy.js
rpm_license: GNU General Public License v3 (GPLv3)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryptpy-0.3.7-1.src.rpm
checksum_type: sha256
description:
  ![Logo](http://mattnappo.com/crypt.png)

  [![Build Status](https://travis-ci.com/mitsukomegumi/CryptPy.js.svg?branch=master)](https://travis-ci.com/mitsukomegumi/CryptPy.js)
  ___

  Dynamically programmable Python botnet (educational purposes only).





  ```BASH
  pip install cryptpy
  ```



  Note: requires installation of cryptpy

  First, `cd payload/vbucks`.

  Build for MacOS:

  1. `npm i`

  2. `yarn dist`

  Build for Windows:

  1. `npm i`

  2. `npm run dist`



  Start a Server:

  ```bash
  cryptpy --server --terminal
  ```

  Start and Register a Bot:

  ```bash
  cryptpy
  ```


pkgId: 98550acb447701456e891d772608540ab3672622f566a6dc056d480ec3e470fb
name: python-crystal-functions
arch: src
version: 2022.12.27
epoch: 0
release: 1
summary: Functions to be used with the CRYSTAL code.
url: https://github.com/crystal-code-tools/crystal_functions
rpm_license: MIT License
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crystal-functions-2022.12.27-1.src.rpm
checksum_type: sha256
description:
  This repository contains functions to be used with the <a href="https://www.crystal.unito.it/index.php">CRYSTAL code</a>.


pkgId: 12b39f26380d28f754cfe05d7b59339b5cb301b3b893fd3cefeb5f6e86363bfb
name: python-crystal-toolkit
arch: src
version: 2024.10.22
epoch: 0
release: 1
summary: Crystal Toolkit is a web app framework from the Materials Project allowing Python
url: https://pypi.org/project/crystal-toolkit/
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crystal-toolkit-2024.10.22-1.src.rpm
checksum_type: sha256
description:
  You can read more about Crystal Toolkit in our preprint, available [here](https://arxiv.org/abs/2302.06147).

  A [citation.cff](CITATION.cff) file is available or a BibTeX citation is as follows:

  ```bibtex
  @article{horton2023crystal,
    title={Crystal Toolkit: A Web App Framework to Improve Usability and Accessibility of Materials Science Research Algorithms},
    author={Horton, Matthew and Shen, Jimmy-Xuan and Burns, Jordan and Cohen, Orion and Chabbey, Fran{\c{c}}ois and Ganose, Alex M and Guha, Rishabh and Huck, Patrick and Li, Hamming Howard and McDermott, Matthew and others},
    journal={arXiv preprint arXiv:2302.06147},
    year={2023}
  }
  ```

  **If you use Crystal Toolkit, either for your own web apps, or by incorporating images generated by Crystal Toolkit in your work, please make sure to cite it appropriately!** Citations are a good way to recognize the contributions of the Crystal Toolkit developers, and allow us to see how it has been used by the community.



  A current list of new contributor issues can be seen [here](https://github.com/materialsproject/crystaltoolkit/labels/new-contributor).
  If you would like a new-contributor issue assigned, get in touch with project maintainers!



  [![Tests](https://github.com/materialsproject/crystaltoolkit/actions/workflows/pytest-docs.yml/badge.svg)](https://github.com/materialsproject/crystaltoolkit/actions/workflows/pytest-docs.yml)
  [![Visual Regression Test Status](https://percy.io/static/images/percy-badge.svg)](https://percy.io/6f28436a/crystaltoolkit)
  [![Docker Build Status](https://img.shields.io/docker/cloud/build/materialsproject/crystaltoolkit)](https://hub.docker.com/repository/docker/materialsproject/crystaltoolkit/general)
  [![Release](https://github.com/materialsproject/crystaltoolkit/actions/workflows/release.yml/badge.svg)](https://github.com/materialsproject/crystaltoolkit/actions/workflows/release.yml)
  [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/materialsproject/crystaltoolkit/main.svg)](https://results.pre-commit.ci/latest/github/materialsproject/crystaltoolkit/main)
  [![arXiv link](https://img.shields.io/badge/arXiv-2302.06147-blue)](https://arxiv.org/abs/2302.06147)



  ```sh
  pip install crystal-toolkit
  ```



  Documentation can be found at [docs.crystaltoolkit.org](https://docs.crystaltoolkit.org).



  | Description                                                                                                                                                                                                                                                                                                                                                               | &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp; |
  | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ |
  | [bandstructure.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/bandstructure.py)<br>  Demonstrates capabilities of the `BandstructureAndDosComponent` component                                                                                                                                                            | [![Launch Codespace]][create codespace]    |
  | [basic_hello_structure.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/basic_hello_structure.py)<br>  Uses `StructureMoleculeComponent` to show a simple `pymatgen` structure                                                                                                                                              | [![Launch Codespace]][create codespace]    |
  | [basic_hello_structure_interactive.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/basic_hello_structure_interactive.py)<br>  Adds a button to the `basic_hello_structure.py` to toggle between two structures interactively                                                                                               | [![Launch Codespace]][create codespace]    |
  | [basic_hello_world.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/basic_hello_world.py)<br>  Bare-bones example showing how to render the text "Hello scientist!" in Dash app                                                                                                                                             | [![Launch Codespace]][create codespace]    |
  | [diffraction.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/diffraction.py)<br>  Demonstrates capabilities of the `XRayDiffractionComponent` component                                                                                                                                                                    | [![Launch Codespace]][create codespace]    |
  | [diffraction_dynamic.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/diffraction_dynamic.py)<br>  Adds a button to `diffraction.py` to load a new structure on the fly                                                                                                                                                     | [![Launch Codespace]][create codespace]    |
  | [diffraction_empty.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/diffraction_empty.py)<br>  Shows `XRayDiffractionComponent` can be mounted without a passing structure                                                                                                                                                  | [![Launch Codespace]][create codespace]    |
  | [kwarg_inputs.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/kwarg_inputs.py)<br>  Shows off boolean input (aka toggle), matrix input and slider input components                                                                                                                                                         | [![Launch Codespace]][create codespace]    |
  | [phase_diagram.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/phase_diagram.py)<br>  Combines `MPRester.get_entries_in_chemsys()` and the `PhaseDiagram` component to plot the Li-O-Co convex hull                                                                                                                        | [![Launch Codespace]][create codespace]    |
  | [pourbaix.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/pourbaix.py)<br>  Combines `MPRester.get_pourbaix_entries()` and the `PourbaixDiagramComponent` to plot the Fe-CO Pourbaix diagram                                                                                                                               | [![Launch Codespace]][create codespace]    |
  | [structure.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/structure.py)<br>  Show cases multiple layout options for the `StructureMoleculeComponent`                                                                                                                                                                      | [![Launch Codespace]][create codespace]    |
  | [structure_magnetic.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/structure_magnetic.py)<br>  Plots a structure with magnetic moments                                                                                                                                                                                    | [![Launch Codespace]][create codespace]    |
  | [matbench_dielectric_structure_on_hover.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/matbench_dielectric_structure_on_hover.py)<br>  Creates a scatter plot hooked up to a `StructureMoleculeComponent` and `DataTable` that show the structure and highlight the table row corresponding to the hovered scatter point. | [![Launch Codespace]][create codespace]    |
  | [matbench_dielectric_datatable_xrd.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/matbench_dielectric_datatable_xrd.py)<br>  Renders a `DataTable` hooked up to a `StructureMoleculeComponent` and `XRayDiffractionComponent` so that hovering a table row will show the corresponding structure and its XRD pattern.     | [![Launch Codespace]][create codespace]    |
  | [transformations.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/transformations.py)<br>  Combines `StructureMoleculeComponent` and `AllTransformationsComponent` to apply interactive structure transformations                                                                                                           | [![Launch Codespace]][create codespace]    |
  | [transformations_minimal.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/transformations_minimal.py)<br>  Shows how to restrict the types of allowed transformations                                                                                                                                                       | [![Launch Codespace]][create codespace]    |
  | [write_structure_screenshot_to_file.py](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/write_structure_screenshot_to_file.py)<br>  Shows to save interactive structure views as image files                                                                                                                                   | [![Launch Codespace]][create codespace]    |

  [Launch Codespace]: https://img.shields.io/badge/Launch-Codespace-darkblue?logo=github
  [create codespace]: https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=98350025



  The [Crystal Toolkit Development Team](https://github.com/materialsproject/crystaltoolkit/graphs/contributors) includes:

  * [Matthew Horton](https://github.com/mkhorton), lead
  * [François Chabbey](<https://github.com/chabb>) for React components
  * [Jimmy Shen](https://github.com/jmmshn) contributed pythreejs support, surface plotting, initial arrows/axes support, and various bug fixes
  * [Joey Montoya](https://github.com/JosephMontoya-TRI) contributed Pourbaix component
  * [Shyam Dwaraknath](https://github.com/shyamd) for planned contributions for ellipsoid support and architectural design
  * [Donny Winston](https://github.com/dwinston), assisted by [Tyler Huntington](https://github.com/tylerhuntington), for helping embed Crystal Toolkit in a Django app
  * [Matt McDermott](https://github.com/mattmcdermott) contributed phase diagram, X-ray Diffraction, X-ray Absorption Spectrum components
  * [Jason Munro](https://github.com/munrojm) contributed band structure component
  * [Janosh Riebesell](https://github.com/janosh) contributed Phonon band structure component, [3 example apps](https://github.com/materialsproject/crystaltoolkit/blob/main/crystal_toolkit/apps/examples/matbench_dielectric_structure_on_hover.py), tests
  * [Stephen Weitzner](https://github.com/sweitzner) contributed POV-Ray integration (in progress)
  * [Richard Tran](https://github.com/CifLord) for contributing plotly-powered Wulff shapes to pymatgen, which Crystal Toolkit uses
  * [Guy Moore](https://github.com/guymoore13) for contributing magnetic moment visualization
  * [Steve Zeltmann](https://github.com/sezelt) for contributing electron diffraction
  * [Patrick Huck](https://github.com/tschaume), releases, operations, bugfixes and POC for MP / MPContribs

  New contributors are welcome, please see our [Code of Conduct](code-of-conduct.md). If you are a new contributor please modify this README in your Pull Request to add your name to the list.



  The Crystal Toolkit repository is home of an object-oriented Python framework for rendering materials science data based on the schema employed by the Materials Project.

  The custom Plotly Dash components that power Crystal Toolkit are now maintained in a [separate repository](https://github.com/materialsproject/dash-mp-components) for ease of development, as well as the [custom React components](https://github.com/materialsproject/mp-react-components). These components were formerly included in the Crystal Toolkit repo, and are still considered part of Crystal Toolkit in spirit.

  There are some [important issues](https://github.com/materialsproject/crystaltoolkit/issues/265) still to be resolved, as well as general improvements to documentation and test suite planned. Some [currently-private code](https://github.com/materialsproject/crystaltoolkit/issues/264) is also planned to be re-incorporated into the public Crystal Toolkit repo.



  Thank you to all the authors and maintainers of the libraries Crystal Toolkit
  depends upon, and in particular [pymatgen](http://pymatgen.org) for crystallographic
  analysis and [Dash from Plotly](https://plot.ly/products/dash/) for their web app framework.

  Thank you to the [NERSC Spin](https://nersc.gov/systems/spin) service for
  hosting the app and for their technical support.



  Please contact @mkhorton with any queries or add an issue on the [GitHub Issues](https://github.com/materialsproject/crystaltoolkit/issues) page.

rpm_license:
  Crystal Toolkit Copyright (c) 2017, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:  (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.  (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.  (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory, U.S. Dept. of Energy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to Lawrence Berkeley National Laboratory or its contributors, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a  non-exclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such enhancements or derivative works thereof, in binary and source code form.


pkgId: d4c1e2dbbfc7cf6ac006bf85aedd49c3420e19ed7cd2fa35750d0178681cdd1a
name: python-crystal4D
arch: src
version: 0.1.7
epoch: 0
release: 1
summary: Deep learning useful information from diffraction images
url: https://pypi.org/project/crystal4D/
rpm_license: MIT License
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crystal4D-0.1.7-1.src.rpm
checksum_type: sha256
description:
  Package for complex convolution network and spectral pooling used for pixel-wise mapping of diffraction space images


pkgId: c560a0442a1da4ae2e86e3b7cfb93eb3ea55b63770234047d4dc3c4c8449bc1a
name: python-crystalpy-barno
arch: src
version: 1.1.14
epoch: 0
release: 1
summary: Python integration with crystal report
url: https://github.com/barno1994/crystalpy_barno
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crystalpy-barno-1.1.14-1.src.rpm
checksum_type: sha256
description:
  * Connect Crystal Report with Python for dynamic output

  To install this project, run the following command:
  ```bash
  pip install crystalpy-barno
  ```


  ```python
  import os
  from datetime import datetime
  from crystalpy_barno import *

  DatabaseConnectionHelper.set_connection_details(
      server_name="SERVER_NAME",
      database_name="DB_NAME",
      user_id="USER",
      password="PASS"
  )
  voucher_no = "VOUCHER_NO"
  pdf_dir = os.path.join(cwd, 'pdf')
  pdf_dir_sales = os.path.join(pdf_dir, 'sales')
  pdf_filepath_sale = os.path.join(pdf_dir_sales, f"{voucher_no}.pdf")

  params = {
      "source_no": "ENTRY_NO",
      "voucher_no": voucher_no,
      "le_code": "AJPL",
      "user_code": "NIM",
      "s_inc_mak": True,
      "s_emp_name": "emp_name_value",
      "le_name": "le_name_value",
      "wh_name": "wh_name_value",
      "wh1": "wh1_value",
      "wh2": "wh2_value",
      "whp1": "whp1_value",
      "whp2": "whp2_value",
      "s_print_hdr": True,
  }
  SaleSnippet.SaleMemo(
      report_filename=os.path.join(cwd, 'rpt/Sale_Memo_IncludeMaking.rpt'),
      output_path=pdf_filepath_sale,
      params=params
  )
  ```

  This code generates a sales memo report using the SaleMemo function from the SaleSnippet module. The report is generated based on the provided parameters and saved as a PDF file in the specified output path.

  The database connection details are set externally using the DatabaseConnectionHelper class. You need to replace the placeholders with your actual database connection details.

  The report template used in this example is Sale_Memo_IncludeMaking.rpt. You can modify this template or use a different one to suit your reporting needs.

  The output directory for the generated report is specified using the pdf_dir variable. You can modify this to change the output directory.

  Contributions are welcome! Please submit a pull request with your changes and a brief description of what you've added or fixed.

  This project is licensed under the MIT License.


pkgId: 2d57e7f38bdab19eaf9c98f7a51ac6d1fde39e125781f4a9c0c7696bde9b11a9
name: python-crystals
arch: src
version: 1.6.2
epoch: 0
release: 1
summary: Data structures for crystallography
url: http://crystals.readthedocs.io
rpm_license: GPLv3
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crystals-1.6.2-1.src.rpm
checksum_type: sha256
description:
  [![Documentation Status](https://readthedocs.org/projects/crystals/badge/?version=master)](https://crystals.readthedocs.io/) [![PyPI Package latest release](https://img.shields.io/pypi/v/crystals.svg)](https://pypi.python.org/pypi/crystals) [![Conda-forge Version](https://img.shields.io/conda/vn/conda-forge/crystals.svg)](https://anaconda.org/conda-forge/crystals) [![DOI badge](https://img.shields.io/badge/DOI-10.1186%2Fs40679--018--0060--y-blue)](https://doi.org/10.1186/s40679-018-0060-y)
  `crystals` is a library of data structure and algorithms to manipulate
  abstract crystals in a Pythonic way. `crystals` helps with reading
  crystallographic files (like .cif and .pdb), provides access to atomic
  positions, scattering utilities, allows for symmetry determination, and
  indexing of diffraction peaks. Although `crystals` can be used on its own,
  it was made to be integrated into larger projects (like
  [scikit-ued](https://github.com/LaurentRDC/scikit-ued)).
  Take a look at the [documentation](https://crystals.readthedocs.io/) for
  more information and examples.


pkgId: c9f7c12ba907c7a5ae0a5d2a5537c421d50452d34dad08ff54c8d0076eb7666d
name: python-crython
arch: src
version: 0.2.0
epoch: 0
release: 1
summary: Lightweight task scheduler using cron expressions.
url: https://github.com/ahawker/crython
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crython-0.2.0-1.src.rpm
checksum_type: sha256
description:
  |Join the chat at https://gitter.im/crython/Lobby|
  |Build Status| |Build status| |codecov| |Code Climate| |Issue Count|
  |PyPI version| |PyPI versions|
  |Stories in Ready|
  crython is a lightweight task (function) scheduler using
  `cron <http://en.wikipedia.org/wiki/Cron>`__ expressions written in
  python.
  Status
  ~~~~~~
  This module is actively maintained.
  Installation
  ~~~~~~~~~~~~
  To install crython from `pip <https://pypi.python.org/pypi/pip>`__:
         $ pip install crython
  To install crython from source:
         $ git clone git@github.com:ahawker/crython.git
         $ python setup.py install
  Usage
  ~~~~~
  Crython supports seven fields (seconds, minutes, hours, day of month,
  month, weekday, year).
  Call a function once a minute:
         import crython
         
         @crython.job(second=0)
         def foo():
             print "... while heavy sack beatings are up a shocking nine hundred percent? - Kent Brockman"
  Call a function every ten seconds:
         
         @crython.job(second=range(0, 60, 10))
         def foo():
             print "I'm a big four-eyed lame-o and I wear the same stupid sweater every day. - Homer's Brain"
  Call a function with a single cron expression:
         
         @crython.job(second='*/10')
         def foo():
             print "Hail to the thee Kamp Krusty... - Kampers"
  Call a function with a full cron expression:
         
         @crython.job(expr='0 0 0 * * 0 *')
         def foo():
             print "Back in line, maggot! - Kearny"
  Call a function with positional and/or keyword arguments:
         
         @job('safety gloves', second='*', name='Homer Simpson')
         def foo(item, name):
             print "Well, I don't need {0}, because I'm {1}. -- Grimey".format(item, name)
  Call a function using `predefined
  keywords <https://github.com/ahawker/crython#keywords>`__:
         
         @crython.job(expr='@daily')
         def foo():
             print "That's where I saw the leprechaun. He tells me to burn things! - Ralph Wiggum"
         
         @crython.job(expr='@reboot')
         def foo():
             print "I call the big one bitey. - Homer Simpson"
  Call a function and run it within a separate thread (default behaviour
  if ``ctx`` is not specified):
         
         @crython.job(expr='@weekly', ctx='thread')
         def foo():
             print "No, no, dig up stupid. - Chief Wiggum"
  Call a function and run it within a separate process:
         
         @crython.job(expr='@hourly', ctx='multiprocess')
         def foo():
             print "Eat my shorts. - Bart Simpson"
  Start the global job scheduler:
         if __name__ == '__main__':
             crython.start()
  Keywords
  ~~~~~~~~
  +-----------------------+-----------------------+-----------------------+
  | Entry                 | Description           | Equivalent To         |
  +=======================+=======================+=======================+
  | @yearly/@annually     | Run once a year at    | 0 0 0 0 1 1 \*        |
  |                       | midnight in the       |                       |
  |                       | morning of January 1  |                       |
  +-----------------------+-----------------------+-----------------------+
  | @monthly              | Run once a month at   | 0 0 0 0 1 \* \*       |
  |                       | midnight in the       |                       |
  |                       | morning of the first  |                       |
  |                       | of the month          |                       |
  +-----------------------+-----------------------+-----------------------+
  | @weekly               | Run once a week at    | 0 0 0 0 \* 0 \*       |
  |                       | midnight in the       |                       |
  |                       | morning of Sunday     |                       |
  +-----------------------+-----------------------+-----------------------+
  | @daily                | Run once a day at     | 0 0 0 \* \* \* \*     |
  |                       | midnight              |                       |
  +-----------------------+-----------------------+-----------------------+
  | @hourly               | Run once an hour at   | 0 0 \* \* \* \* \*    |
  |                       | the beginning of the  |                       |
  |                       | hour                  |                       |
  +-----------------------+-----------------------+-----------------------+
  | @minutely             | Run once a minute at  | 0 \* \* \* \* \* \*   |
  |                       | the beginning of the  |                       |
  |                       | minute                |                       |
  +-----------------------+-----------------------+-----------------------+
  | @reboot               | Run once at startup   | @reboot               |
  +-----------------------+-----------------------+-----------------------+
  TODO
  ~~~~
  -  Support “L”, “W” and “#” specials.
  -  Determine time delta from now -> next time expression is valid.
  Contributing
  ~~~~~~~~~~~~
  If you would like to contribute, simply fork the repository, push your
  changes and send a pull request.
  License
  ~~~~~~~
  Crython is available under the `MIT
  license <https://github.com/ahawker/crython/blob/master/LICENSE.md>`__.
  See Other
  ~~~~~~~~~
  There are similar python cron libraries out there. See:
  `pycron <http://www.kalab.com/freeware/pycron/pycron.htm>`__,
  `python-crontab <http://pypi.python.org/pypi/python-crontab/>`__,
  `cronex <https://github.com/jameseric/cronex>`__.


pkgId: 88ee6f0a913e59aa3865114a6343619e88516a2b5666b04ba65638f23a294307
name: python-crytic-compile
arch: src
version: 0.3.7
epoch: 0
release: 1
summary: Util to facilitate smart contracts compilation.
url: https://github.com/crytic/crytic-compile
rpm_license: AGPL-3.0
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-crytic-compile-0.3.7-1.src.rpm
checksum_type: sha256
description:
  [![Build Status](https://img.shields.io/github/actions/workflow/status/crytic/crytic-compile/ci.yml?branch=master)](https://github.com/crytic/crytic-compile/actions?query=workflow%3ACI)
  [![Slack Status](https://slack.empirehacking.nyc/badge.svg)](https://slack.empirehacking.nyc)
  [![PyPI version](https://badge.fury.io/py/crytic-compile.svg)](https://badge.fury.io/py/crytic-compile)

  Library to help smart contract compilation. It includes support for:
  - Direct solc compilation
  - [Foundry](https://github.com/foundry-rs/foundry/)
  - [Hardhat](https://github.com/nomiclabs/hardhat)
  - [Brownie](https://github.com/iamdefinitelyahuman/brownie)
  - [Buidler](https://github.com/nomiclabs/buidler)
  - [Dapp](https://dapp.tools/dapp/)
  - [Embark](https://embark.status.im/)
  - [Etherlime](https://github.com/LimeChain/etherlime)
  - [Etherscan](https://etherscan.io/) (including several alt-chain explorers and testnets)
  - [Truffle](https://truffleframework.com/)
  - [Waffle](https://github.com/EthWorks/Waffle)

  To force compilation with a specific framework, use the `--compile-force-framework` flag. For example, to force compilation with Hardhat:

  ```shell
  crytic-compile . --compile-force-framework hardhat
  ```

  See the [Configuration](https://github.com/crytic/crytic-compile/wiki/Configuration) documentation for advanced usages.

  The plugin is used in Trail of Bits tools, including:
  - [Slither](https://github.com/crytic/slither)
  - [Echidna](https://github.com/crytic/echidna)
  - [Manticore](https://github.com/trailofbits/manticore/)
  - [evm-cfg-builder](https://github.com/crytic/evm_cfg_builder)




  ```shell
  pip3 install crytic-compile
  ```



  In the root directory of your project e.g. same directory as `hardhat.config.js` or `foundry.toml`, run:

  ```shell
  crytic-compile .
  ```

  Crytic-compile will generate `crytic-export/contracts.json` containing the AST/ABI and bytecodes of the contracts.

  Run `crytic-compile --help` for more options.



  If your project uses [libraries](https://docs.soliditylang.org/en/latest/contracts.html#libraries) with external functions, they can be linked to their deployed address with the `--compile-libraries` flag. For example, if you have a library `SafeMath` deployed at `0xff`, you can link it with:


  ```shell
  crytic-compile . --compile-libraries "(SafeMath, 0xff)"
  ```

  If you are fuzzing with Echidna or Medusa, follow this [tutorial on linking libraries](https://secure-contracts.com/program-analysis/echidna/advanced/working-with-libraries.html?highlight=library#linking-libraries).



  See the [library documentation](https://github.com/crytic/crytic-compile/wiki/Library-Documentation).


pkgId: 17f92ae1d440a1156dc8880aab7bf594e17507a32203b432d07e2962ad699d3f
name: python-cryton
arch: src
version: 2.1.1
epoch: 0
release: 1
summary: Advanced scenario orchestrator
url: https://gitlab.ics.muni.cz/cryton
rpm_license: MIT
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cryton-2.1.1-1.src.rpm
checksum_type: sha256
description:
  [![pipeline status](https://gitlab.ics.muni.cz/cryton/cryton/badges/master/pipeline.svg)](https://gitlab.ics.muni.cz/cryton/cryton/-/commits/master)
  [![coverage report](https://gitlab.ics.muni.cz/cryton/cryton/badges/master/coverage.svg)](https://gitlab.ics.muni.cz/cryton/cryton/-/commits/master)
  [![Latest Release](https://gitlab.ics.muni.cz/cryton/cryton/-/badges/release.svg)](https://gitlab.ics.muni.cz/cryton/cryton/-/releases)

  [//]: # (TODO: add badges for python versions, black, pylint, flake8, unit tests, integration tests)


  Cryton is a Cron-like red team framework for complex attack scenarios automation and scheduling. It provides ways to plan, execute, and evaluate multistep attacks.

  **Main features**:

  - [Metasploit](https://github.com/rapid7/metasploit-framework) integration
  - [Empire](https://github.com/BC-SECURITY/Empire) integration
  - Reliable remote execution
  - Reproducible scenarios
  - Set of modules (Nmap, Medusa, FFUF, ...) with machine-readable output

  Cryton is tested and targeted primarily on **Debian** and **Kali Linux**. Please keep in mind that **only
  the latest version is supported** and issues regarding different OS or distributions may **not** be resolved.

  For more information see the [documentation](https://cryton.gitlab-pages.ics.muni.cz/).


  Please, follow the guide in the [documentation](https://cryton.gitlab-pages.ics.muni.cz/cryton/latest/quick-start/).


  Please, follow the guide in the [documentation](https://cryton.gitlab-pages.ics.muni.cz/cryton/latest/development/).


pkgId: 7d0f0aca32fba647a8cb1611805bc85a6b6cea7290e0f84a29d7bb79d7707ec7
name: python-cs
arch: src
version: 3.2.0
epoch: 0
release: 1
summary: A simple yet powerful CloudStack API client for Python and the command-line.
url: https://github.com/exoscale/cs
rpm_license: BSD
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-3.2.0-1.src.rpm
checksum_type: sha256
description:
      pip install cs
      
      pip install cs[highlight]
      
      pip install cs[async]
      
      pip install cs[async,highlight]


pkgId: 0ae59e74c5b0d272948eedf84d7db8758329c4fbbc656c2e50686bcc4e94f083
name: python-cs-app-osx-defaults
arch: src
version: 20241122
epoch: 0
release: 1
summary: Access the MacOS defaults via the `defaults` command.
url: https://pypi.org/project/cs-app-osx-defaults/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-app-osx-defaults-20241122-1.src.rpm
checksum_type: sha256
description:
  Access the MacOS defaults via the `defaults` command.

  *Latest release 20241122*:
  run: drop quiet=False parameter, the run() inside defaults() will pick things up.



  A view of the defaults.

  *`Defaults.domains`*:
  Return a list of the domains present in the defaults.

  *`Defaults.run(self, argv, doit=True) -> str`*:
  Run a `defaults` subcommand, return the output decoded from UTF-8.



  Run the `defaults` command with the arguments `argv`.
  If the optional `host` parameter is supplied,
  a value of `'.'` uses the `-currentHost` option
  and other values are used with the `-host` option.
  Return the `CompletedProcess` result or `None` if `doit` is false.



  A view of the defaults for a particular domain.

  *`DomainDefaults.as_dict(self)`*:
  Return the current defaults as a `dict`.

  *`DomainDefaults.flush(self)`*:
  Forget any cached information.





  *Release 20241122*:
  run: drop quiet=False parameter, the run() inside defaults() will pick things up.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240201*:
  Initial PyPI release.


pkgId: 26db6e69a1a5384e02e6d84f9e9171fb4cfc7c71682d8c106fafec622a69e9c9
name: python-cs-cache
arch: src
version: 20241122
epoch: 0
release: 1
summary: A few caching data structures and other lossy things with capped sizes.
url: https://pypi.org/project/cs-cache/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-cache-20241122-1.src.rpm
checksum_type: sha256
description:
  A few caching data structures and other lossy things with capped sizes.

  *Latest release 20241122*:
  * convof, ConvCache.convof: new options force=False parameter to bypass existing cache entries.
  * ConvCache.convof: check the conv_subpath using cs.fs.validate_rpath.



  A caching front end for another mapping.
  This is intended as a generic superclass for a proxy to a
  slower mapping such as a database or remote key value store.

  Note that this subclasses `MultiOpenMixin` to start/stop the worker `Thread`.
  Users must enclose use of a `CachingMapping` in a `with` statement.
  If subclasses also subclass `MultiOpenMixin` their `startup_shutdown`
  method needs to also call our `startup_shutdown` method.

  Example:

      class Store:
        """ A key value store with a slower backend.
        """
        def __init__(self, mapping:Mapping):
          self.mapping = CachingMapping(mapping)

      .....
      S = Store(slow_mapping)
      with S:
        ... work with S ...

  *`CachingMapping.__init__(self, mapping: Mapping, *, max_size=1024, queue_length=1024, delitem_bg: Optional[Callable[[Any], cs.result.Result]] = None, setitem_bg: Optional[Callable[[Any, Any], cs.result.Result]] = None, missing_fallthrough: bool = False)`*:
  Initialise the cache.

  Parameters:
  * `mapping`: the backing store, a mapping
  * `max_size`: optional maximum size for the cache, default 1024
  * `queue_length`: option size for the queue to the worker, default 1024
  * `delitem_bg`: optional callable to queue a delete of a
    key in the backing store; if unset then deleted are
    serialised in the worker thread
  * `setitem_bg`: optional callable to queue setting the value
    for a key in the backing store; if unset then deleted are
    serialised in the worker thread
  * `missing_fallthrough`: is true (default `False`) always
    fall back to the backing mapping if a key is not in the cache

  *`CachingMapping.flush(self)`*:
  Wait for outstanding requests in the queue to complete.
  Return the UNIX time of completion.

  *`CachingMapping.items(self)`*:
  Generator yielding `(k,v)` pairs.

  *`CachingMapping.keys(self)`*:
  Generator yielding the keys.



  A cache for conversions of file contents such as thumbnails
  or transcoded media, etc. This keeps cached results in a file
  tree based on a content key, whose default function is
  `cs.hashutils.file_checksum('blake3')`.

  *`ConvCache.__init__(self, fspath: Optional[str] = None, content_key_func=None)`*:
  Initialise a `ConvCache`.

  Parameters:
  * `fspath`: optional base path of the cache, default from
    `ConvCache.DEFAULT_CACHE_BASEPATH`;
    if this does not exist it will be created using `os.mkdir`
  * `content_key_func`: optional function to compute a key
    from the contents of a file, default `cs.hashindex.file_checksum`
    (the blake3 hash of the contents)

  *`ConvCache.content_key(self, srcpath)`*:
  Return a content key for the filesystem path `srcpath`.

  *`ConvCache.content_subpath(self, srcpath) -> str`*:
  Return the content key based subpath component.

  This default assumes the content key is a hash code and
  breaks it hex representation into a 3 level hierarchy
  such as `'d6/d9/c510785c468c9aa4b7bda343fb79'`.

  *`ConvCache.convof(self, srcpath, conv_subpath, conv_func, *, ext=None, force=False) -> str`*:
  Return the filesystem path of the cached conversion of
  `srcpath` via `conv_func`.

  Parameters:
  * `srcpath`: the source filesystem path
  * `conv_subpath`: a name for the conversion which encompasses
    the salient aspaects such as `'png/64/64'` for a 64x64 pixel
    thumbnail in PNG format
  * `conv_func`: a callable of the form `conv_func(srcpath,dstpath)`
    to convert the contents of `srcpath` and write the result
    to the filesystem path `dstpath`
  * `ext`: an optional filename extension, default from the
    first component of `conv_subpath`
  * `force`: option flag to require conversion even if the
    cache has an entry



  `ConvCache.convof` using the default cache.



  A simple least recently used cache.

  Unlike `functools.lru_cache`
  this provides `on_add` and `on_remove` callbacks.

  *`LRU_Cache.__init__(self, max_size, *, on_add=None, on_remove=None)`*:
  Initialise the LRU_Cache with maximum size `max`,
  additon callback `on_add` and removal callback `on_remove`.

  *`LRU_Cache.__delitem__(self, key)`*:
  Delete the specified `key`, calling the on_remove callback.

  *`LRU_Cache.__setitem__(self, key, value)`*:
  Store the item in the cache. Prune if necessary.

  *`LRU_Cache.flush(self)`*:
  Clear the cache.

  *`LRU_Cache.get(self, key, default=None)`*:
  Mapping method: get value for `key` or `default`.

  *`LRU_Cache.items(self)`*:
  Items from the cache.

  *`LRU_Cache.keys(self)`*:
  Keys from the cache.



  Enhanced workalike of @functools.lru_cache.





  *Release 20241122*:
  * convof, ConvCache.convof: new options force=False parameter to bypass existing cache entries.
  * ConvCache.convof: check the conv_subpath using cs.fs.validate_rpath.

  *Release 20240422.1*:
  ConvCache docstring update.

  *Release 20240422*:
  New ConvCache and convof: a cache for conversions of file contents such as thumbnails or transcoded media.

  *Release 20240412*:
  * New CachingMapping, a caching front end for another mapping.
  * LRU_Cache: add keys() and items().

  *Release 20181228*:
  Initial PyPI release.


pkgId: 5a79e1619dd84384c7d6c3141ef3f3b46e2d095411cd31f51e034b19f4d09b9b
name: python-cs-cmdutils
arch: src
version: 20241122
epoch: 0
release: 1
url: https://pypi.org/project/cs-cmdutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-cmdutils-20241122-1.src.rpm
checksum_type: sha256
summary:
  Convenience functions for working with the cmd stdlib module, the BaseCommand class for constructing command line programmes, and other command line related stuff.

description:
  Convenience functions for working with the cmd stdlib module,
  the BaseCommand class for constructing command line programmes,
  and other command line related stuff.

  *Latest release 20241122*:
  * BaseCommand.cmd_help: new -l (long), -r (recurse) and -s (short, not long) options.
  * A big rework of the help/usage output; still one rough edge left.
  * BaseCommandOptions.popopts: infill with False instead of None for missing boolean options.
  * SubCommand: a few new methods and properties to expose various operations.
  * BaseCommand._prerun_setup: revert SUBCOMMAND_ARGV_DEFAULT to None, make a missing subcommand run "help -s" when there's no SUBCOMMAND_ARGV_DEFAULT.



  A base class for handling nestable command lines.

  This class provides the basic parse and dispatch mechanisms
  for command lines.
  To implement a command line
  one instantiates a subclass of `BaseCommand`:

      class MyCommand(BaseCommand):
          GETOPT_SPEC = 'ab:c'
          USAGE_FORMAT = r"""and provides either a `main` method if the command has no subcommands
  or a suite of `cmd_`*subcommand* methods, one per subcommand.

  Running a command is done by:

      MyCommand(argv).run()

  Modules which implement a command line mode generally look like this:

      ... imports etc ...
      def main(argv=None, **run_kw):
          """ The command line mode.
          """
          return MyCommand(argv).run(**run_kw)
      ... other code ...
      class MyCommand(BaseCommand):
      ... other code ...
      if __name__ == '__main__':
          sys.exit(main(sys.argv))

  Instances have a `self.options` attribute on which optional
  modes are set,
  avoiding conflict with the attributes of `self`.

  Subclasses with no subcommands
  generally just implement a `main(argv)` method.

  Subclasses with subcommands
  should implement a `cmd_`*subcommand*`(argv)` instance method
  for each subcommand.
  If a subcommand is itself implemented using `BaseCommand`
  then it can be a simple attribute:

      cmd_subthing = SubThingCommand

  Returning to methods, if there is a paragraph in the method docstring
  commencing with `Usage:` then that paragraph is incorporated
  into the main usage message automatically.
  Example:

      def cmd_ls(self, argv):
          """ Usage: {cmd} [paths...]
                Emit a listing for the named paths.

              Further docstring non-usage information here.
          """
          ... do the "ls" subcommand ...

  The subclass is customised by overriding the following methods:
  * `apply_opt(opt,val)`:
    apply an individual getopt global command line option
    to `self.options`.
  * `apply_opts(opts)`:
    apply the `opts` to `self.options`.
    `opts` is an `(option,value)` sequence
    as returned by `getopot.getopt`.
    The default implementation iterates over these and calls `apply_opt`.
  * `run_context()`:
    a context manager to provide setup or teardown actions
    to occur before and after the command implementation respectively,
    such as to open and close a database.
  * `cmd_`*subcmd*`(argv)`:
    if the command line options are followed by an argument
    whose value is *subcmd*,
    then the method `cmd_`*subcmd*`(subcmd_argv)`
    will be called where `subcmd_argv` contains the command line arguments
    following *subcmd*.
  * `main(argv)`:
    if there are no `cmd_`*subcmd*` methods then method `main(argv)`
    will be called where `argv` contains the command line arguments.

  Editorial: why not arparse?
  Primarily because when incorrectly invoked
  an argparse command line prints the help/usage messgae
  and aborts the whole programme with `SystemExit`.
  But also, I find the whole argparse `add_argument` thing cumbersome.

  Usage summary:

      Usage: base [common-options...] [-a] [-b bvalue] [-c] [--] arguments...
          -a    Do it all.
          -b    But using bvalue.
          -c    The 'c' option!
        """
        ...
        Subcommands:
          help [common-options...] [-l] [-s] [subcommand-names...]
            Print help for subcommands.
            This outputs the full help for the named subcommands,
            or the short help for all subcommands if no names are specified.
            -l  Long help.
            -r  Recurse into subcommands.
            -s  Short help.
          info [common-options...] [field-names...]
            Recite general information.
            Explicit field names may be provided to override the default listing.
          shell [common-options...]
            Run a command prompt via cmd.Cmd using this command's subcommands.

  *`BaseCommand.__init__(self, argv=None, *, cmd=None, options=None, **kw_options)`*:
  Initialise the command line.
  Raises `GetoptError` for unrecognised options.

  Parameters:
  * `argv`:
    optional command line arguments
    including the main command name if `cmd` is not specified.
    The default is `sys.argv`.
    The contents of `argv` are copied,
    permitting desctructive parsing of `argv`.
  * `cmd`:
    optional keyword specifying the command name for context;
    if this is not specified it is taken from `argv.pop(0)`.
  * `options`:
    an optional keyword providing object for command state and context.
    If not specified a new `self.Options` instance
    is allocated for use as `options`.
    The default `Options` class is `BaseCommandOptions`,
    a dataclass with some prefilled attributes and properties
    to aid use later.
  Other keyword arguments are applied to `self.options`
  as attributes.

  The `cmd` and `argv` parameters have some fiddly semantics for convenience.
  There are 3 basic ways to initialise:
  * `BaseCommand()`: `argv` comes from `sys.argv`
    and the value for `cmd` is derived from `argv[0]`
  * `BaseCommand(argv)`: `argv` is the complete command line
    including the command name and the value for `cmd` is
    derived from `argv[0]`
  * `BaseCommand(argv, cmd=foo)`: `argv` is the command
    arguments _after_ the command name and `cmd` is set to
    `foo`

  The command line arguments are parsed according to
  the optional `GETOPT_SPEC` class attribute (default `''`).
  If `getopt_spec` is not empty
  then `apply_opts(opts)` is called
  to apply the supplied options to the state
  where `opts` is the return from `getopt.getopt(argv,getopt_spec)`.

  After the option parse,
  if the first command line argument *foo*
  has a corresponding method `cmd_`*foo*
  then that argument is removed from the start of `argv`
  and `self.cmd_`*foo*`(argv,options,cmd=`*foo*`)` is called
  and its value returned.
  Otherwise `self.main(argv,options)` is called
  and its value returned.

  If the command implementation requires some setup or teardown
  then this may be provided by the `run_context`
  context manager method,
  called with `cmd=`*subcmd* for subcommands
  and with `cmd=None` for `main`.

  *`BaseCommand.Options`*

  *`BaseCommand.SubCommandClass`*

  *`BaseCommand.apply_opt(self, opt, val)`*:
  Handle an individual global command line option.

  This default implementation raises a `RuntimeError`.
  It only fires if `getopt` actually gathered arguments
  and would imply that a `GETOPT_SPEC` was supplied
  without an `apply_opt` or `apply_opts` method to implement the options.

  *`BaseCommand.apply_opts(self, opts)`*:
  Apply command line options.

  Subclasses can override this
  but it is usually easier to override `apply_opt(opt,val)`.

  *`BaseCommand.apply_preargv(self, argv)`*:
  Do any preparsing of `argv` before the subcommand/main-args.
  Return the remaining arguments.

  This default implementation applies the default options
  supported by `self.options` (an instance of `self.Options`
  class).

  *`BaseCommand.cmd_help(self, argv)`*:
  Usage: {cmd} [-l] [-s] [subcommand-names...]
  Print help for subcommands.
  This outputs the full help for the named subcommands,
  or the short help for all subcommands if no names are specified.
  -l  Long help.
  -r  Recurse into subcommands.
  -s  Short help.

  *`BaseCommand.cmd_info(self, argv, *, field_names=None, skip_names=None)`*:
  Usage: {cmd} [field-names...]
    Recite general information.
    Explicit field names may be provided to override the default listing.

  This default method recites the values from `self.options`,
  excluding the basic fields from `BaseCommandOptions` other
  than `cmd` and `dry_run`.

  This base method provides two optional parameters to allow
  subclasses to tune its behaviour:
  - `field_namees`: an explicit list of options attributes to print
  - `skip_names`: a list of option attributes to not print
    if `field_names` is not specified; the default is the
    field names of `BaseCommandOptions` excepting `cmd` and
    `dry_run`

  *`BaseCommand.cmd_shell(*a, upd: Optional[cs.upd.Upd] = <function uses_upd.<locals>.<lambda> at 0x10fc63d90>, **kw)`*:
  Usage: {cmd}
  Run a command prompt via cmd.Cmd using this command's subcommands.

  *`BaseCommand.cmdloop(self, intro=None)`*:
  Use `cmd.Cmd` to run a command loop which calls the `cmd_`* methods.

  *`BaseCommand.extract_usage(cmd=None)`*:
  Extract the `Usage:` paragraph from `cls__doc__` if present.
  Return a 2-tuple of `(doc_without_usage,usage_text)`
  being the remaining docstring and a full usage message.

  *`BaseCommand.getopt_error_handler(cmd, options, e, usage, subcmd=None)`*:
  The `getopt_error_handler` method
  is used to control the handling of `GetoptError`s raised
  during the command line parse
  or during the `main` or `cmd_`*subcmd*` calls.

  This default handler issues a warning containing the exception text,
  prints the usage message to standard error,
  and returns `True` to indicate that the error has been handled.

  The handler is called with these parameters:
  * `cmd`: the command name
  * `options`: the `options` object
  * `e`: the `GetoptError` exception
  * `usage`: the command usage or `None` if this was not provided
  * `subcmd`: optional subcommand name;
    if not `None`, is the name of the subcommand which caused the error

  It returns a true value if the exception is considered handled,
  in which case the main `run` method returns 2.
  It returns a false value if the exception is considered unhandled,
  in which case the main `run` method reraises the `GetoptError`.

  To let the exceptions out unhandled
  this can be overridden with a method which just returns `False`.

  Otherwise,
  the handler may perform any suitable action
  and return `True` to contain the exception
  or `False` to cause the exception to be reraised.

  *`BaseCommand.handle_signal(self, sig, frame, *, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x10fc63640>)`*:
  The default signal handler, which cancels the default `RunState`.

  *`BaseCommand.has_subcommands()`*:
  Test whether the class defines additional subcommands.

  *`BaseCommand.method_cmdname(method_name: str)`*:
  The `cmd` value from a method name.

  *`BaseCommand.poparg(argv: List[str], *specs, unpop_on_error: bool = False, opt_spec_class=None)`*:
  Pop the leading argument off `argv` and parse it.
  Return the parsed argument.
  Raises `getopt.GetoptError` on a missing or invalid argument.

  This is expected to be used inside a `main` or `cmd_*`
  command handler method or inside `apply_preargv`.

  You can just use:

      value = argv.pop(0)

  but this method provides conversion and valuation
  and a richer failure mode.

  Parameters:
  * `argv`: the argument list, which is modified in place with `argv.pop(0)`
  * the argument list `argv` may be followed by some help text
    and/or an argument parser function.
  * `validate`: an optional function to validate the parsed value;
    this should return a true value if valid,
    or return a false value or raise a `ValueError` if invalid
  * `unvalidated_message`: an optional message after `validate`
    for values failing the validation
  * `unpop_on_error`: optional keyword parameter, default `False`;
    if true then push the argument back onto the front of `argv`
    if it fails to parse; `GetoptError` is still raised

  Typical use inside a `main` or `cmd_*` method might look like:

      self.options.word = self.poparg(argv, int, "a count value")
      self.options.word = self.poparg(
          argv, int, "a count value",
         lambda count: count > 0, "count should be positive")

  Because it raises `GetoptError` on a bad argument
  the normal usage message failure mode follows automatically.

  Demonstration:

      >>> argv = ['word', '3', 'nine', '4']
      >>> BaseCommand.poparg(argv, "word to process")
      'word'
      >>> BaseCommand.poparg(argv, int, "count value")
      3
      >>> BaseCommand.poparg(argv, float, "length")
      Traceback (most recent call last):
        ...
      getopt.GetoptError: length 'nine': float('nine'): could not convert string to float: 'nine'
      >>> BaseCommand.poparg(argv, float, "width", lambda width: width > 5)
      Traceback (most recent call last):
        ...
      getopt.GetoptError: width '4': invalid value
      >>> BaseCommand.poparg(argv, float, "length")
      Traceback (most recent call last):
        ...
      getopt.GetoptError: length: missing argument
      >>> argv = ['-5', 'zz']
      >>> BaseCommand.poparg(argv, float, "size", lambda size: size > 0, "size should be >0")
      Traceback (most recent call last):
        ...
      getopt.GetoptError: size '-5': size should be >0
      >>> argv  # -5 was still consumed
      ['zz']
      >>> BaseCommand.poparg(argv, float, "size2", unpop_on_error=True)
      Traceback (most recent call last):
        ...
      getopt.GetoptError: size2 'zz': float('zz'): could not convert string to float: 'zz'
      >>> argv  # zz was pushed back
      ['zz']

  *`BaseCommand.popopts(self, argv, options, **opt_specs)`*:
  OBSOLETE popopts

  A convenience shim which returns `self.options.popopts(argv,**opt_specs)`.

  *`BaseCommand.repl(self, *argv, banner=None, local=None)`*:
  Run an interactive Python prompt with some predefined local names.
  Aka REPL (Read Evaluate Print Loop).

  Parameters:
  * `argv`: any notional command line arguments
  * `banner`: optional banner string
  * `local`: optional local names mapping

  The default `local` mapping is a `dict` containing:
  * `argv`: from `argv`
  * `options`: from `self.options`
  * `self`: from `self`
  * the attributes of `options`
  * the attributes of `self`

  This is not presented automatically as a subcommand, but
  commands wishing such a command should provide something
  like this:

      def cmd_repl(self, argv):
          """ Usage: {cmd}
                Run an interactive Python prompt with some predefined local names.
          """
          return self.repl(*argv)

  *`BaseCommand.run(self, **kw_options)`*:
  Run a command.
  Returns the exit status of the command.
  May raise `GetoptError` from subcommands.

  Any keyword arguments are used to override `self.options` attributes
  for the duration of the run,
  for example to presupply a shared `Upd` from an outer context.

  If the first command line argument *foo*
  has a corresponding method `cmd_`*foo*
  then that argument is removed from the start of `argv`
  and `self.cmd_`*foo*`(cmd=`*foo*`)` is called
  and its value returned.
  Otherwise `self.main(argv)` is called
  and its value returned.

  If the command implementation requires some setup or teardown
  then this may be provided by the `run_context()`
  context manager method.

  *`BaseCommand.run_context(*a, upd: Optional[cs.upd.Upd] = <function uses_upd.<locals>.<lambda> at 0x10fc63880>, **kw)`*:
  The context manager which surrounds `main` or `cmd_`*subcmd*.
  Normally this will have a bare `yield`, but you can yield
  a not `None` value to exit before the command, for example
  if the run setup fails.

  This default does several things, and subclasses should
  override it like this:

      @contextmanager
      def run_context(self):
        with super().run_context():
          try:
            ... subclass context setup ...
              yield
          finally:
            ... any unconditional cleanup ...

  This base implementation does the following things:
  - provides a `self.options` which is a copy of the original
    `self.options` so that it may freely be modified during the
    command
  - provides a prevailing `RunState` as `self.options.runstate`
    if one is not already present
  - provides a `cs.upd.Upd` context for status lines
  - catches `self.options.runstate_signals` and handles them
    with `self.handle_signal`

  *`BaseCommand.subcommand`*

  *`BaseCommand.subcommand_usage_text(self, subcmd, usage_format_mapping=None, short=False)`*:
  Return the usage text for a subcommand.

  *`BaseCommand.subcommands`*

  *`BaseCommand.usage_text(self, **subcommand_kw)`*:
  Compute the "Usage:" message for this class.
  Parameters are as for `SubCommand.usage_text`.



  A `cmd.Cmd` subclass used to provide interactive use of a
  command's subcommands.

  The `BaseCommand.cmdloop()` class method instantiates an
  instance of this and calls its `.cmdloop()` method
  i.e. `cmd.Cmd.cmdloop`.



  A base class for the `BaseCommand` `options` object.

  This is the default class for the `self.options` object
  available during `BaseCommand.run()`,
  and available as the `BaseCommand.Options` attribute.

  Any keyword arguments are applied as field updates to the instance.

  It comes prefilled with:
  * `.dry_run=False`
  * `.force=False`
  * `.quiet=False`
  * `.verbose=False`
  and a `.doit` property which is the inverse of `.dry_run`.

  It is recommended that if `BaseCommand` subclasses use a
  different type for their `Options` that it should be a
  subclass of `BaseCommandOptions`.
  Since `BaseCommandOptions` is a data class, this typically looks like:

      @dataclass
      class Options(BaseCommand.Options):
          ... optional extra fields etc ...

  *`BaseCommandOptions.__call__(self, **updates)`*:
  Calling the options object returns a context manager whose
  value is a shallow copy of the options with any `suboptions` applied.

  Example showing the semantics:

      >>> from cs.cmdutils import BaseCommandOptions
      >>> @dataclass
      ... class DemoOptions(BaseCommandOptions):
      ...   x: int = 0
      ...
      >>> options = DemoOptions(x=1)
      >>> assert options.x == 1
      >>> assert not options.verbose
      >>> with options(verbose=True) as subopts:
      ...     assert options is not subopts
      ...     assert options.x == 1
      ...     assert not options.verbose
      ...     assert subopts.x == 1
      ...     assert subopts.verbose
      ...
      >>> assert options.x == 1
      >>> assert not options.verbose

  *`BaseCommandOptions.as_dict(self)`*:
  Return the options as a `dict`.
  This contains all the public attributes of `self`.

  *`BaseCommandOptions.copy(self, **updates)`*:
  Return a new instance of `BaseCommandOptions` (well, `type(self)`)
  which is a shallow copy of the public attributes from `self.__dict__`.

  Any keyword arguments are applied as attribute updates to the copy.

  *`BaseCommandOptions.doit`*:
  I usually use a `doit` flag, the inverse of `dry_run`.

  *`BaseCommandOptions.fields_as_dict(self)`*:
  Return the options' fields as a `dict`.
  This contains all the field values of `self`.

  *`BaseCommandOptions.getopt_spec_map(opt_specs_kw: Mapping)`*:
  Return a 3-tuple of (shortopts,longopts,getopt_spec_map)` being:
  - `shortopts`: the `getopt()` short options specification string
  - `longopts`: the `getopts()` long option specification list
  - `getopt_spec_map`: a mapping of `opt`->`OptionSpec`
    where `opt` is as from `opt,val` from `getopt()`
    and `opt_spec` is the associated `OptionSpec` instance

  *`BaseCommandOptions.opt_spec_class`*

  *`BaseCommandOptions.perthread_state`*

  *`BaseCommandOptions.popopts(self, argv, **opt_specs_kw)`*:
  Parse option switches from `argv`, a list of command line strings
  with leading option switches and apply them to `self`.
  Modify `argv` in place.

  Example use in a `BaseCommand` `cmd_foo` method:

      def cmd_foo(self, argv):
          options = self.options
          options.popopts(
              c_='config',
              l='long',
              x='trace',
          )
          if self.options.dry_run:
              print("dry run!")

  The expected options are specified by the keyword parameters
  in `opt_specs`.
  Each keyword name has the following semantics:
  * options not starting with a letter may be preceeded by an underscore
    to allow use in the parameter list, for example `_1='once'`
    for a `-1` option setting the `once` option name
  * a single letter name specifies a short option
    and a multiletter name specifies a long option
  * options requiring an argument have a trailing underscore
  * options not requiring an argument normally imply a value
    of `True`; if their synonym commences with a dash they will
    imply a value of `False`, for example `n='dry_run',y='-dry_run'`.

  The `BaseCommand` class provides a `popopts` method
  which is a shim for this method applied to its `.options`.
  So common use in a command method usually looks like this:

      class SomeCommand(BaseCommand):

          def cmd_foo(self, argv):
              
              self.popopts(argv, jobs=1, j='jobs')
              print("jobs =", self.options.jobs)

  The `self.options` object is preprovided as an instance of
  the `self.Options` class, which is `BaseCommandOptions` by
  default. There is presupplies support for some basic options
  like `-v` for "verbose" and so forth, and a subcommand
  need not describe these in a call to `self.options.popopts()`.

  Example:

      >>> import os.path
      >>> from typing import Optional
      >>> @dataclass
      ... class DemoOptions(BaseCommandOptions):
      ...   all: bool = False
      ...   jobs: int = 1
      ...   number: int = 0
      ...   once: bool = False
      ...   path: Optional[str] = None
      ...   trace_exec: bool = False
      ...
      >>> options = DemoOptions()
      >>> argv = ['-1', '-v', '-y', '-j4', '--path=/foo', 'bah', '-x']
      >>> opt_dict = options.popopts(
      ...   argv,
      ...   _1='once',
      ...   a='all',
      ...   j_=('jobs',int),
      ...   x='-trace_exec',
      ...   y='-dry_run',
      ...   dry_run=None,
      ...   path_=(str, os.path.isabs, 'not an absolute path'),
      ...   verbose=None,
      ... )
      >>> opt_dict
      {'once': True, 'verbose': True, 'dry_run': False, 'jobs': 4, 'path': '/foo'}
      >>> options # doctest: +ELLIPSIS
      DemoOptions(cmd=None, dry_run=False, force=False, quiet=False, runstate_signals=(...), verbose=True, all=False, jobs=4, number=0, once=True, path='/foo', trace_exec=False)

  *`BaseCommandOptions.update(self, **updates)`*:
  Modify the options in place with the mapping `updates`.
  It would be more normal to call the options in a `with` statement
  as shown for `__call__`.



  Decorator for `cmd.Cmd` subclass methods
  to supply some basic quality of service.

  This decorator:
  - wraps the function call in a `cs.pfx.Pfx` for context
  - intercepts `getopt.GetoptError`s, issues a `warning`
    and runs `self.do_help` with the method name,
    then returns `None`
  - intercepts other `Exception`s,
    issues an `exception` log message
    and returns `None`

  The intended use is to decorate `cmd.Cmd` `do_`* methods:

      from cmd import Cmd
      from cs.cmdutils import docmd
      ...
      class MyCmd(Cmd):
          @docmd
          def do_something(...):
              ... do something ...



  Extract a `"Usage:"`paragraph from a docstring
  and return the unindented usage and the docstring with that paragraph elided.

  If the usage paragraph is not present, return `(None,doc)`.



  A class to support parsing an option value.

  *`OptionSpec.__post_init__(self)`*:
  Infer `field_name` and `help_text` if unspecified.

  *`OptionSpec.add_argument(self, parser, options=None)`*:
  Add this option to an `argparser`-style option parser.
  The optional `options` parameter may be used to supply an
  `Options` instance to provide a default value.

  *`OptionSpec.from_opt_kw(opt_k: str, specs: Union[str, List, Tuple, NoneType])`*:
  Factory to produce an `OptionSpec` from a `(key,specs)` 2-tuple
  as from the `items()` from a `popopts()` call.

  The `specs` is normally a list or tuple, but a bare string
  will be promoted to a 1-element list containing the string.

  The elements of `specs` are considered in order for:
  - an identifier specifying the `arg_name`,
    optionally prepended with a dash to indicate an inverted option
  - a help text about the option
  - a callable to parse the option string value to obtain the actual value
  - a callable to validate the option value
  - a message for use when validation fails

  *`OptionSpec.getopt_long`*:
  The option specification for a getopt long option.
  Return `None` if `self.opt_name` is only 1 character.

  *`OptionSpec.getopt_opt`*:
  The `opt` we expect from `opt,val=getopt(argv,...)`.

  *`OptionSpec.getopt_short`*:
  The option specification for a getopt short option.
  Return `''` if `self.opt_name` is longer than 1 character.

  *`OptionSpec.help_text_from_field_name(field_name)`*:
  Compute an inferred `help_text` from an option `field_name`.

  *`OptionSpec.needs_arg`*:
  Whether we expect an argument: we have a `self.arg_name`.

  *`OptionSpec.option_terse(self)`*:
  Return the `"-x"` or `"--name"` option string (with the arg name if expected).

  *`OptionSpec.option_usage(self)`*:
  A 2 line usage entry for this option.
  Example:

      -j jobs
        Job limit.

  *`OptionSpec.parse_value(self, value)`*:
  Parse `value` according to the spec.
  Raises `GetoptError` for invalid values.



  Call `print()` if `options.verbose` and not `options.quiet`.



  An implementation for a subcommand.

  *`SubCommand.__call__(self, argv: List[str])`*:
  Run the subcommand.

  Parameters:
  * `argv`: the command line arguments after the subcommand name

  *`SubCommand.get_cmd(self) -> str`*:
  Return the `cmd` string for this `SubCommand`,
  derived from the subcommand's method name or class name
  if `self.cmd` is not set.

  *`SubCommand.get_subcmds(self)`*:
  Return the names of `self.method`'s subcommands in lexical order.

  *`SubCommand.get_subcommands(self)`*:
  Return `self.method`'s mapping of subcommand name to `SubCommand`.

  *`SubCommand.get_usage_format(self, show_common=False) -> str`*:
  Return the usage format string for this subcommand.
  *Note*: no leading "Usage:" prefix.
  If `show_common` is true, include the `Common options:` paragraph.

  *`SubCommand.get_usage_keywords(self, *, cmd: Optional[str] = None, usage_mapping: Optional[Mapping] = None) -> str`*:
  Return a mapping to be used when formatting the usage format string.

  This is an elaborate `ChainMap` of:
  - the optional `cmd` or `self.get_cmd()`
  - the optional `usage_mapping`
  - `self.usage_mapping`
  - `self.method.USAGE_KEYWORDS` if present
  - the attributes of `self.command`
  - the attributes of `type(self.command)`
  - the attributes of the module for `type(self.command)`

  *`SubCommand.has_subcommands`*:
  Whether this `SubCommand`'s `.method` has subcommands.

  *`SubCommand.instance`*:
  An instance of the class for `self.method`.

  *`SubCommand.short_subusages(self, subcmds: List[str])`*:
  Return a list of tabulated one line subcommand summaries.

  *`SubCommand.usage_text(self, *, cmd=None, short: bool, recurse: bool = False, show_common: bool = False, show_subcmds: Union[bool, str, List[str], NoneType] = None, usage_mapping: Optional[Mapping] = None, seen_subcommands: Optional[Mapping] = None) -> str`*:
  Return the filled out usage text for this subcommand.



  Call `print()` if `options.verbose`.
  This is a compatibility shim for `qvprint()` with `quiet=False`.





  *Release 20241122*:
  * BaseCommand.cmd_help: new -l (long), -r (recurse) and -s (short, not long) options.
  * A big rework of the help/usage output; still one rough edge left.
  * BaseCommandOptions.popopts: infill with False instead of None for missing boolean options.
  * SubCommand: a few new methods and properties to expose various operations.
  * BaseCommand._prerun_setup: revert SUBCOMMAND_ARGV_DEFAULT to None, make a missing subcommand run "help -s" when there's no SUBCOMMAND_ARGV_DEFAULT.

  *Release 20241119*:
  * SubCommand.usage_text: small improvements and a fix.
  * New BaseCommand.SubCommandClass attribute for the SubCommand subclass, supports using additional SubCommand subclasses for eg cs.djutils.

  *Release 20241117*:
  * _COMMON_OPT_SPECS: include a --dry-run option, alter for -n.
  * SubCommand.usage_text: one line listing for common subcommands.

  *Release 20241110*:
  * Various fixes and enhancements to the usage text generation.
  * BaseCommandOptions.COMMON_OPT_SPECS stuff, including inheritance and presentation in usage.
  * BaseCommand.run: if self.run_context() yields not None, exit immediately with that value.
  * Rationalise and improve OptionSpec.
  * Supplant vprint with qvprint which honours quiet and verbose, provide vprint as a shim with a hardwired quiet=False.
  * Make cmd_info the default subcommand and make some improvements.
  * BaseCommandOptions: new fields_as_dict method, update copy() to start with fields_as_dict, overlay as_dict, overlay updates.
  * OptionSpec: new add_argument(parser[,options]) method to add this option to an argparse style option parser (again, for the upcoming cs.djutils module).
  * A heap of small things.

  *Release 20241007*:
  * BaseCommand: drop __init_subclass__, was doing too much to early.
  * BaseCommand.__init__: no cmd and argv[0] ends with ".py" infer cmd from the class name.
  * Other minor updates.

  *Release 20241005*:
  * BaseCommand: provide an options.runstate to allow the command to override the default (almost never happens).
  * BaseCommand.popopts: fix reference to OptionSpec class.
  * BaseCommand.run: put the try/except:CancellationError outside the self.run_context context manager call, as it can directly raise CancellationError.
  * New vprint() function calling print() if options.verbose.

  *Release 20240709*:
  BaseCommand: support putting the top level Usage in the class docstring instead of as .USAGE_FORMAT, append full usage to the class docstring in `BaseCommand.__init_subclass__`.

  *Release 20240630*:
  * BaseCommand: make SUBCOMMAND_ARGV_DEFAULT be 'shell' for an interactive prompt, still a little unsure how sensible this is, aiming at the very cool submode stuff from the Cisco switch config command line.
  * BaseCommandOptions: new as_dict() method.
  * New SubCommand.usage_text() to compose the full usage text for this SubCommand.
  * Many small improvements and internal refactors.

  *Release 20240519*:
  BaseCommand.run_context: attach the runstate to the options.

  *Release 20240422*:
  * BaseCommandOptions.popopts: return the dict from BaseCommand.popopts().
  * BaseCommand.apply_preargv: apply the default options supported by self.options.
  * BaseCommandOptions.update(mapping) method, useful for dropping subcommand-specific defaults onto the options ahead of the local popopts() call.

  *Release 20240412*:
  * BaseCommand.run_context: do not store .upd and .runstate on the options (it confuses options in subcommands and we have @uses_runstate and @uses_upd forthis anyway these days).
  * BaseCommand.run_context: catch SIGQUIT, present the default handler as BaseCommand.handle_signal.

  *Release 20240316*:
  * New @uses_cmd_options decorator to provide an "options" parameter being the prevailing BaseCommandOptions instance.
  * BaseCommandOptions.popopts: get common options from BaseCommandOptions.COMMON_OPT_SPECS.

  *Release 20240211*:
  * Include the first sentence of the subcommand description in the short help.
  * BaseCommandOptions: move the runstate_signals into this directly.
  * BaseCommand: move the run() options stuff into run_context() and have it work on a copy of the original options.
  * BaseCommandCmd: implement get_names(), provide docstrings for the do_* attributes, thus help.
  * BaseCommand.run_context: make runstate and upd keyword only parameters.

  *Release 20240201*:
  * BaseCommand.run: catch CancellationError and return 1.
  * BaseCommandCmd.__getattr__: recognise EOF, exit and quit to terminate the cmdloop.

  *Release 20231129*:
  BaseCommandOptions: define a runstate field.

  *Release 20230703*:
  Small internal changes.

  *Release 20230612*:
  * BaseCommand.cmdloop: fix intro parameter.
  * Other small fixes.

  *Release 20230407*:
  * BaseCommand: use @uses_runstate when preparing the command, store as self.options.runstate.
  * Make BaseCommandOptions a data class.
  * Drop any pretence at python 2 support, we're long past that.
  * BaseCommand: new cmdloop method to run a cmd.Cmd instance to run subcommand interactively.
  * BaseCommand: rename shell to repl, add cmd_shell to call cmdloop().
  * Drop BaseCommand.apply_defaults in favour of the Options dataclass.
  * BaseCommand: do setup_logging before initiating the Options instance.

  *Release 20230212*:
  * BaseCommand.run_context: update RunState support.
  * BaseCommand.run_context: always be having an self.options.upd.

  *Release 20230211*:
  BaseCommand: new shell() method to present an interactive Python prompt for use by subclasses cmd_shell method if desired.

  *Release 20221228*:
  Move a lot of the context logic from BaseCommand.run to BaseCommand.run_context, which now must be correctly overridden in subclasses.

  *Release 20220918*:
  * BaseCommand.run_context: expand default signals to include SIGHUP, expose as BaseCommand.DEFAULT_SIGNALS.
  * BaseCommand.run: pass in the subclass handle_signal method if present.

  *Release 20220626*:
  * BaseCommand.poparg: fix positional argument handling.
  * BaseCommand.poparg: new unpop_on_error=False parameter to support pushing a bad argument back onto the front of the argument list.

  *Release 20220606*:
  BaseCommand.run: remove the Upd bodge, too annoying, now fixed in cs.upd I believe.

  *Release 20220605*:
  * BaseCommand: new popopts(argv,...) compact getopt wrapper.
  * BaseCommand: new poparg(argv,...) compact validating argument consumer.
  * BaseCommand: drop run_argv, provided no utility.
  * BaseCommand.run: get the RunState signal list from self.options.runstate_signals.
  * BaseCommand.apply_opts: support multiple individual options raising GetoptError, as I hate commands which abort at the first bad option.
  * Assorted other small things.

  *Release 20220429*:
  * BaseCommand: fold dots in argv[0] into underscores, supports subcommands like "setup.py".
  * BaseCommand: new popargv(argv[,help_text[,parse[,validate[,unvalidated_message]]]]) helper class method.
  * BaseCommand: accept dashed-form of the underscored_form subcommand name.
  * BaseCommand: new self.options.runstate_signals=SIGINT,SIGTERM specifying singals to catch-and-cancel, shuffle run() context managers.

  *Release 20220318*:
  BaseCommand.__init__: handle main() method in the New Scheme.

  *Release 20220315*:
  _BaseSubCommand.__init__: hook in the class USAGE_KEYWORDS for methods.

  *Release 20220311*:
  BaseCommand: big refactor of subcommand internals and make the "cmd_foo=FooCommand" implementation work properly.

  *Release 20211208*:
  BaseCommand: better handle an unknown subcommand.

  *Release 20210927*:
  * Usage: show only the per subcommand usage for in-subcommand GetoptError.
  * Usage: show terse usage when the subcommand cannot be recognised.
  * Usage: support bare -h, -help, --help.

  *Release 20210913*:
  New BaseCommand.apply_preargv method to gather special arguments before subcommands.

  *Release 20210906*:
  * BaseCommand.cmd_help: bugfix obsolete parameter list.
  * BaseCommand.SUBCOMMAND_ARGV_DEFAULT: support a single str value, turn into list.

  *Release 20210809*:
  Bugfix BaseCommand.cmd_help for modern API.

  *Release 20210731*:
  * BaseCommand.run: apply optional keyword arguments to self.options during the run.
  * Look for self.SUBCOMMAND_ARGV_DEFAULT if no subcommand is supplied.
  * Bugfix case for "main" method and no "cmd_*" methods.
  * Bugfix BaseCommand.cmd_help.

  *Release 20210420*:
  * BaseCommand.getopt_error_handler: replace error print() with warning().
  * Docstring improvements.

  *Release 20210407.1*:
  BaseCommand: bugfix for __init_subclass__ docstring update.

  *Release 20210407*:
  * BaseCommand.__init_subclass__: behave sanely if the subclass has no initial __doc__.
  * BaseCommand: new .run_argv convenience method, obviates the "def main" boilerplate.

  *Release 20210404*:
  BaseCommand subclasses: automatically add the main usage message to the subclass docstring.

  *Release 20210306*:
  * BREAKING CHANGE: rework BaseCommand as a more normal class instantiated with argv and with most methods being instance methods, getting the former `options` parameter from self.options.
  * BaseCommand: provide default `apply_opt` and `apply_opts` methods; subclasses will generally just override the former.

  *Release 20210123*:
  BaseCommand: propagate the format mapping (cmd, USAGE_KEYWORDS) to the subusage generation.

  *Release 20201102*:
  * BaseCommand.cmd_help: supply usage only for "all commands", full docstring for specified commands.
  * BaseCommand: honour presupplied options.log_level.
  * BaseCommand.usage_text: handle missing USAGE_FORMAT better.
  * BaseCommand.run: provide options.upd.
  * BaseCommand subclasses may now override BaseCommand.OPTIONS_CLASS (default SimpleNamespace) in order to provide convenience methods on the options.
  * BaseCommand.run: separate variable for subcmd with dash translated to underscore to match method names.
  * Minor fixes.

  *Release 20200615*:
  BaseCommand.usage_text: do not mention the "help" command if it is the only subcommand (it won't be available if there are no other subcommands).

  *Release 20200521.1*:
  Fix DISTINFO.install_requires.

  *Release 20200521*:
  * BaseCommand.run: support using BaseCommand subclasses as cmd_* names to make it easy to nest BaseCommands.
  * BaseCommand: new hack_postopts_argv method called after parsing the main command line options, for inferring subcommands or the like.
  * BaseCommand: extract "Usage:" paragraphs from subcommand method docstrings to build the main usage message.
  * BaseCommand: new cmd_help default command.
  * Assorted bugfixes and small improvements.

  *Release 20200318*:
  * BaseCommand.run: make argv optional, get additional usage keywords from self.USAGE_KEYWORDS.
  * @BaseCommand.add_usage_to_docstring: honour cls.USAGE_KEYWORDS.
  * BaseCommand: do not require GETOPT_SPEC for commands with no defined options.
  * BaseCommand.run: call cs.logutils.setup_logging.

  *Release 20200229*:
  Improve subcommand selection logic, replace StackableValues with stackattrs, drop `cmd` from arguments passed to main/cmd_* methods (present in `options`).

  *Release 20200210*:
  * New BaseCommand.add_usage_to_docstring class method to be called after class setup, to append the usage message to the class docstring.
  * BaseCommand.run: remove spurious Pfx(cmd), as logutils does this for us already.

  *Release 20190729*:
  BaseCommand: support for a USAGE_FORMAT usage message format string and a getopt_error_handler method.

  *Release 20190619.1*:
  Another niggling docstring formatting fix.

  *Release 20190619*:
  Minor documentation updates.

  *Release 20190617.2*:
  Lint.

  *Release 20190617.1*:
  Initial release with @docmd decorator and alpha quality BaseCommand command line assistance class.


pkgId: 87faf99bf0d7aed81985ae9efba478397f1652bb4bfefa30ffdd35ed207af631
name: python-cs-cmdutils
arch: src
version: 20241206
epoch: 0
release: 1
url: https://pypi.org/project/cs-cmdutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-cmdutils-20241206-1.src.rpm
checksum_type: sha256
summary:
  Convenience functions for working with the cmd stdlib module, the BaseCommand class for constructing command line programmes, and other command line related stuff.

description:
  Convenience functions for working with the cmd stdlib module,
  the BaseCommand class for constructing command line programmes,
  and other command line related stuff.

  *Latest release 20241206*:
  * New @popopts decorator to run self.options.popopts and fill out the usage message.
  * Add "-e ssh-command" and the .ssh_exe attribute to BaseCommandOptions.



  A base class for handling nestable command lines.

  This class provides the basic parse and dispatch mechanisms
  for command lines.
  To implement a command line
  one instantiates a subclass of `BaseCommand`:

      class MyCommand(BaseCommand):
          GETOPT_SPEC = 'ab:c'
          USAGE_FORMAT = r"""and provides either a `main` method if the command has no subcommands
  or a suite of `cmd_`*subcommand* methods, one per subcommand.

  Running a command is done by:

      MyCommand(argv).run()

  Modules which implement a command line mode generally look like this:

      ... imports etc ...
      def main(argv=None, **run_kw):
          """ The command line mode.
          """
          return MyCommand(argv).run(**run_kw)
      ... other code ...
      class MyCommand(BaseCommand):
      ... other code ...
      if __name__ == '__main__':
          sys.exit(main(sys.argv))

  Instances have a `self.options` attribute on which optional
  modes are set,
  avoiding conflict with the attributes of `self`.

  Subclasses with no subcommands
  generally just implement a `main(argv)` method.

  Subclasses with subcommands
  should implement a `cmd_`*subcommand*`(argv)` instance method
  for each subcommand.
  If a subcommand is itself implemented using `BaseCommand`
  then it can be a simple attribute:

      cmd_subthing = SubThingCommand

  Returning to methods, if there is a paragraph in the method docstring
  commencing with `Usage:` then that paragraph is incorporated
  into the main usage message automatically.
  Example:

      def cmd_ls(self, argv):
          """ Usage: {cmd} [paths...]
                Emit a listing for the named paths.

              Further docstring non-usage information here.
          """
          ... do the "ls" subcommand ...

  The subclass is customised by overriding the following methods:
  * `apply_opt(opt,val)`:
    apply an individual getopt global command line option
    to `self.options`.
  * `apply_opts(opts)`:
    apply the `opts` to `self.options`.
    `opts` is an `(option,value)` sequence
    as returned by `getopot.getopt`.
    The default implementation iterates over these and calls `apply_opt`.
  * `run_context()`:
    a context manager to provide setup or teardown actions
    to occur before and after the command implementation respectively,
    such as to open and close a database.
  * `cmd_`*subcmd*`(argv)`:
    if the command line options are followed by an argument
    whose value is *subcmd*,
    then the method `cmd_`*subcmd*`(subcmd_argv)`
    will be called where `subcmd_argv` contains the command line arguments
    following *subcmd*.
  * `main(argv)`:
    if there are no `cmd_`*subcmd*` methods then method `main(argv)`
    will be called where `argv` contains the command line arguments.

  Editorial: why not arparse?
  Primarily because when incorrectly invoked
  an argparse command line prints the help/usage messgae
  and aborts the whole programme with `SystemExit`.
  But also, I find the whole argparse `add_argument` thing cumbersome.

  Usage summary:

      Usage: base [common-options...] [-a] [-b bvalue] [-c] [--] arguments...
          -a    Do it all.
          -b    But using bvalue.
          -c    The 'c' option!
        """
        ...
        Subcommands:
          help [common-options...] [-l] [-s] [subcommand-names...]
            Print help for subcommands.
            This outputs the full help for the named subcommands,
            or the short help for all subcommands if no names are specified.
            Options:
              -l  Long listing.
              -r  Recurse into subcommands.
              -s  Short listing.
          info [common-options...] [field-names...]
            Recite general information.
            Explicit field names may be provided to override the default listing.
          shell [common-options...]
            Run a command prompt via cmd.Cmd using this command's subcommands.

  *`BaseCommand.__init__(self, argv=None, *, cmd=None, options=None, **kw_options)`*:
  Initialise the command line.
  Raises `GetoptError` for unrecognised options.

  Parameters:
  * `argv`:
    optional command line arguments
    including the main command name if `cmd` is not specified.
    The default is `sys.argv`.
    The contents of `argv` are copied,
    permitting desctructive parsing of `argv`.
  * `cmd`:
    optional keyword specifying the command name for context;
    if this is not specified it is taken from `argv.pop(0)`.
  * `options`:
    an optional keyword providing object for command state and context.
    If not specified a new `self.Options` instance
    is allocated for use as `options`.
    The default `Options` class is `BaseCommandOptions`,
    a dataclass with some prefilled attributes and properties
    to aid use later.
  Other keyword arguments are applied to `self.options`
  as attributes.

  The `cmd` and `argv` parameters have some fiddly semantics for convenience.
  There are 3 basic ways to initialise:
  * `BaseCommand()`: `argv` comes from `sys.argv`
    and the value for `cmd` is derived from `argv[0]`
  * `BaseCommand(argv)`: `argv` is the complete command line
    including the command name and the value for `cmd` is
    derived from `argv[0]`
  * `BaseCommand(argv, cmd=foo)`: `argv` is the command
    arguments _after_ the command name and `cmd` is set to
    `foo`

  The command line arguments are parsed according to
  the optional `GETOPT_SPEC` class attribute (default `''`).
  If `getopt_spec` is not empty
  then `apply_opts(opts)` is called
  to apply the supplied options to the state
  where `opts` is the return from `getopt.getopt(argv,getopt_spec)`.

  After the option parse,
  if the first command line argument *foo*
  has a corresponding method `cmd_`*foo*
  then that argument is removed from the start of `argv`
  and `self.cmd_`*foo*`(argv,options,cmd=`*foo*`)` is called
  and its value returned.
  Otherwise `self.main(argv,options)` is called
  and its value returned.

  If the command implementation requires some setup or teardown
  then this may be provided by the `run_context`
  context manager method,
  called with `cmd=`*subcmd* for subcommands
  and with `cmd=None` for `main`.

  *`BaseCommand.Options`*

  *`BaseCommand.SubCommandClass`*

  *`BaseCommand.apply_opt(self, opt, val)`*:
  Handle an individual global command line option.

  This default implementation raises a `NotImplementedError`.
  It only fires if `getopt` actually gathered arguments
  and would imply that a `GETOPT_SPEC` was supplied
  without an `apply_opt` or `apply_opts` method to implement the options.

  *`BaseCommand.apply_opts(self, opts)`*:
  Apply command line options.

  Subclasses can override this
  but it is usually easier to override `apply_opt(opt,val)`.

  *`BaseCommand.apply_preargv(self, argv)`*:
  Do any preparsing of `argv` before the subcommand/main-args.
  Return the remaining arguments.

  This default implementation applies the default options
  supported by `self.options` (an instance of `self.Options`
  class).

  *`BaseCommand.cmd_help(self, argv)`*:
  Usage: {cmd} [-l] [-s] [subcommand-names...]
  Print help for subcommands.
  This outputs the full help for the named subcommands,
  or the short help for all subcommands if no names are specified.
  Options:
    -l  Long listing.
    -r  Recurse into subcommands.
    -s  Short listing.

  *`BaseCommand.cmd_info(self, argv, *, field_names=None, skip_names=None)`*:
  Usage: {cmd} [field-names...]
    Recite general information.
    Explicit field names may be provided to override the default listing.

  This default method recites the values from `self.options`,
  excluding the basic fields from `BaseCommandOptions` other
  than `cmd` and `dry_run`.

  This base method provides two optional parameters to allow
  subclasses to tune its behaviour:
  - `field_namees`: an explicit list of options attributes to print
  - `skip_names`: a list of option attributes to not print
    if `field_names` is not specified; the default is the
    field names of `BaseCommandOptions` excepting `cmd` and
    `dry_run`

  *`BaseCommand.cmd_shell(*a, upd: Optional[cs.upd.Upd] = <function uses_upd.<locals>.<lambda> at 0x10b8b5c60>, **kw)`*:
  Usage: {cmd}
  Run a command prompt via cmd.Cmd using this command's subcommands.

  *`BaseCommand.cmdloop(self, intro=None)`*:
  Use `cmd.Cmd` to run a command loop which calls the `cmd_`* methods.

  *`BaseCommand.extract_usage(cmd=None)`*:
  Extract the `Usage:` paragraph from `cls__doc__` if present.
  Return a 2-tuple of `(doc_without_usage,usage_text)`
  being the remaining docstring and a full usage message.

  *Note*: this actually sets `cls.USAGE_FORMAT` if that does
  not already exist.

  *`BaseCommand.getopt_error_handler(cmd, options, e, usage, subcmd=None)`*:
  The `getopt_error_handler` method
  is used to control the handling of `GetoptError`s raised
  during the command line parse
  or during the `main` or `cmd_`*subcmd*` calls.

  This default handler issues a warning containing the exception text,
  prints the usage message to standard error,
  and returns `True` to indicate that the error has been handled.

  The handler is called with these parameters:
  * `cmd`: the command name
  * `options`: the `options` object
  * `e`: the `GetoptError` exception
  * `usage`: the command usage or `None` if this was not provided
  * `subcmd`: optional subcommand name;
    if not `None`, is the name of the subcommand which caused the error

  It returns a true value if the exception is considered handled,
  in which case the main `run` method returns 2.
  It returns a false value if the exception is considered unhandled,
  in which case the main `run` method reraises the `GetoptError`.

  To let the exceptions out unhandled
  this can be overridden with a method which just returns `False`.

  Otherwise,
  the handler may perform any suitable action
  and return `True` to contain the exception
  or `False` to cause the exception to be reraised.

  *`BaseCommand.handle_signal(self, sig, frame, *, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x10b8b56c0>)`*:
  The default signal handler, which cancels the default `RunState`.

  *`BaseCommand.has_subcommands()`*:
  Test whether the class defines additional subcommands.

  *`BaseCommand.method_cmdname(method_name: str)`*:
  The `cmd` value from a method name.

  *`BaseCommand.poparg(argv: List[str], *specs, unpop_on_error: bool = False, opt_spec_class=None)`*:
  Pop the leading argument off `argv` and parse it.
  Return the parsed argument.
  Raises `getopt.GetoptError` on a missing or invalid argument.

  This is expected to be used inside a `main` or `cmd_*`
  command handler method or inside `apply_preargv`.

  You can just use:

      value = argv.pop(0)

  but this method provides conversion and valuation
  and a richer failure mode.

  Parameters:
  * `argv`: the argument list, which is modified in place with `argv.pop(0)`
  * the argument list `argv` may be followed by some help text
    and/or an argument parser function.
  * `validate`: an optional function to validate the parsed value;
    this should return a true value if valid,
    or return a false value or raise a `ValueError` if invalid
  * `unvalidated_message`: an optional message after `validate`
    for values failing the validation
  * `unpop_on_error`: optional keyword parameter, default `False`;
    if true then push the argument back onto the front of `argv`
    if it fails to parse; `GetoptError` is still raised

  Typical use inside a `main` or `cmd_*` method might look like:

      self.options.word = self.poparg(argv, int, "a count value")
      self.options.word = self.poparg(
          argv, int, "a count value",
         lambda count: count > 0, "count should be positive")

  Because it raises `GetoptError` on a bad argument
  the normal usage message failure mode follows automatically.

  Demonstration:

      >>> argv = ['word', '3', 'nine', '4']
      >>> BaseCommand.poparg(argv, "word to process")
      'word'
      >>> BaseCommand.poparg(argv, int, "count value")
      3
      >>> BaseCommand.poparg(argv, float, "length")
      Traceback (most recent call last):
        ...
      getopt.GetoptError: length 'nine': float('nine'): could not convert string to float: 'nine'
      >>> BaseCommand.poparg(argv, float, "width", lambda width: width > 5)
      Traceback (most recent call last):
        ...
      getopt.GetoptError: width '4': invalid value
      >>> BaseCommand.poparg(argv, float, "length")
      Traceback (most recent call last):
        ...
      getopt.GetoptError: length: missing argument
      >>> argv = ['-5', 'zz']
      >>> BaseCommand.poparg(argv, float, "size", lambda size: size > 0, "size should be >0")
      Traceback (most recent call last):
        ...
      getopt.GetoptError: size '-5': size should be >0
      >>> argv  # -5 was still consumed
      ['zz']
      >>> BaseCommand.poparg(argv, float, "size2", unpop_on_error=True)
      Traceback (most recent call last):
        ...
      getopt.GetoptError: size2 'zz': float('zz'): could not convert string to float: 'zz'
      >>> argv  # zz was pushed back
      ['zz']

  *`BaseCommand.popopts(self, argv, options, **opt_specs)`*:
  OBSOLETE popopts

  A convenience shim which returns `self.options.popopts(argv,**opt_specs)`.

  *`BaseCommand.repl(self, *argv, banner=None, local=None)`*:
  Run an interactive Python prompt with some predefined local names.
  Aka REPL (Read Evaluate Print Loop).

  Parameters:
  * `argv`: any notional command line arguments
  * `banner`: optional banner string
  * `local`: optional local names mapping

  The default `local` mapping is a `dict` containing:
  * `argv`: from `argv`
  * `options`: from `self.options`
  * `self`: from `self`
  * the attributes of `options`
  * the attributes of `self`

  This is not presented automatically as a subcommand, but
  commands wishing such a command should provide something
  like this:

      def cmd_repl(self, argv):
          """ Usage: {cmd}
                Run an interactive Python prompt with some predefined local names.
          """
          return self.repl(*argv)

  *`BaseCommand.run(self, **kw_options)`*:
  Run a command.
  Returns the exit status of the command.
  May raise `GetoptError` from subcommands.

  Any keyword arguments are used to override `self.options` attributes
  for the duration of the run,
  for example to presupply a shared `Upd` from an outer context.

  If the first command line argument *foo*
  has a corresponding method `cmd_`*foo*
  then that argument is removed from the start of `argv`
  and `self.cmd_`*foo*`(cmd=`*foo*`)` is called
  and its value returned.
  Otherwise `self.main(argv)` is called
  and its value returned.

  If the command implementation requires some setup or teardown
  then this may be provided by the `run_context()`
  context manager method.

  *`BaseCommand.run_context(*a, upd: Optional[cs.upd.Upd] = <function uses_upd.<locals>.<lambda> at 0x10b8b54e0>, **kw)`*:
  The context manager which surrounds `main` or `cmd_`*subcmd*.
  Normally this will have a bare `yield`, but you can yield
  a not `None` value to exit before the command, for example
  if the run setup fails.

  This default does several things, and subclasses should
  override it like this:

      @contextmanager
      def run_context(self):
        with super().run_context():
          try:
            ... subclass context setup ...
              yield
          finally:
            ... any unconditional cleanup ...

  This base implementation does the following things:
  - provides a `self.options` which is a copy of the original
    `self.options` so that it may freely be modified during the
    command
  - provides a prevailing `RunState` as `self.options.runstate`
    if one is not already present
  - provides a `cs.upd.Upd` context for status lines
  - catches `self.options.runstate_signals` and handles them
    with `self.handle_signal`

  *`BaseCommand.subcommand`*

  *`BaseCommand.subcommand_usage_text(self, subcmd, usage_format_mapping=None, short=False)`*:
  Return the usage text for a subcommand.

  *`BaseCommand.subcommands`*

  *`BaseCommand.usage_text(self, **subcommand_kw)`*:
  Compute the "Usage:" message for this class.
  Parameters are as for `SubCommand.usage_text`.



  A `cmd.Cmd` subclass used to provide interactive use of a
  command's subcommands.

  The `BaseCommand.cmdloop()` class method instantiates an
  instance of this and calls its `.cmdloop()` method
  i.e. `cmd.Cmd.cmdloop`.



  A base class for the `BaseCommand` `options` object.

  This is the default class for the `self.options` object
  available during `BaseCommand.run()`,
  and available as the `BaseCommand.Options` attribute.

  Any keyword arguments are applied as field updates to the instance.

  It comes prefilled with:
  * `.dry_run=False`
  * `.force=False`
  * `.quiet=False`
  * `.ssh_exe='ssh'`
  * `.verbose=False`
  and a `.doit` property which is the inverse of `.dry_run`.

  It is recommended that if `BaseCommand` subclasses use a
  different type for their `Options` that it should be a
  subclass of `BaseCommandOptions`.
  Since `BaseCommandOptions` is a data class, this typically looks like:

      @dataclass
      class Options(BaseCommand.Options):
          ... optional extra fields etc ...

  *`BaseCommandOptions.__call__(self, **updates)`*:
  Calling the options object returns a context manager whose
  value is a shallow copy of the options with any `suboptions` applied.

  Example showing the semantics:

      >>> from cs.cmdutils import BaseCommandOptions
      >>> @dataclass
      ... class DemoOptions(BaseCommandOptions):
      ...   x: int = 0
      ...
      >>> options = DemoOptions(x=1)
      >>> assert options.x == 1
      >>> assert not options.verbose
      >>> with options(verbose=True) as subopts:
      ...     assert options is not subopts
      ...     assert options.x == 1
      ...     assert not options.verbose
      ...     assert subopts.x == 1
      ...     assert subopts.verbose
      ...
      >>> assert options.x == 1
      >>> assert not options.verbose

  *`BaseCommandOptions.as_dict(self)`*:
  Return the options as a `dict`.
  This contains all the public attributes of `self`.

  *`BaseCommandOptions.copy(self, **updates)`*:
  Return a new instance of `BaseCommandOptions` (well, `type(self)`)
  which is a shallow copy of the public attributes from `self.__dict__`.

  Any keyword arguments are applied as attribute updates to the copy.

  *`BaseCommandOptions.doit`*:
  I usually use a `doit` flag, the inverse of `dry_run`.

  *`BaseCommandOptions.fields_as_dict(self)`*:
  Return the options' fields as a `dict`.
  This contains all the field values of `self`.

  *`BaseCommandOptions.getopt_spec_map(opt_specs_kw: Mapping, common_opt_specs=None)`*:
  Return a 3-tuple of (shortopts,longopts,getopt_spec_map)` being:
  - `shortopts`: the `getopt()` short options specification string
  - `longopts`: the `getopts()` long option specification list
  - `getopt_spec_map`: a mapping of `opt`->`OptionSpec`
    where `opt` is as from `opt,val` from `getopt()`
    and `opt_spec` is the associated `OptionSpec` instance

  *`BaseCommandOptions.opt_spec_class`*

  *`BaseCommandOptions.perthread_state`*

  *`BaseCommandOptions.popopts(self, argv, **opt_specs_kw)`*:
  Parse option switches from `argv`, a list of command line strings
  with leading option switches and apply them to `self`.
  Modify `argv` in place.

  Example use in a `BaseCommand` `cmd_foo` method:

      def cmd_foo(self, argv):
          options = self.options
          options.popopts(
              c_='config',
              l='long',
              x='trace',
          )
          if self.options.dry_run:
              print("dry run!")

  The expected options are specified by the keyword parameters
  in `opt_specs`.
  Each keyword name has the following semantics:
  * options not starting with a letter may be preceeded by an underscore
    to allow use in the parameter list, for example `_1='once'`
    for a `-1` option setting the `once` option name
  * a single letter name specifies a short option
    and a multiletter name specifies a long option
  * options requiring an argument have a trailing underscore
  * options not requiring an argument normally imply a value
    of `True`; if their synonym commences with a dash they will
    imply a value of `False`, for example `n='dry_run',y='-dry_run'`.

  The `BaseCommand` class provides a `popopts` method
  which is a shim for this method applied to its `.options`.
  So common use in a command method usually looks like this:

      class SomeCommand(BaseCommand):

          def cmd_foo(self, argv):
              
              self.popopts(argv, jobs=1, j='jobs')
              print("jobs =", self.options.jobs)

  The `self.options` object is preprovided as an instance of
  the `self.Options` class, which is `BaseCommandOptions` by
  default. There is presupplies support for some basic options
  like `-v` for "verbose" and so forth, and a subcommand
  need not describe these in a call to `self.options.popopts()`.

  Example:

      >>> import os.path
      >>> from typing import Optional
      >>> @dataclass
      ... class DemoOptions(BaseCommandOptions):
      ...   all: bool = False
      ...   jobs: int = 1
      ...   number: int = 0
      ...   once: bool = False
      ...   path: Optional[str] = None
      ...   trace_exec: bool = False
      ...
      >>> options = DemoOptions()
      >>> argv = ['-1', '-v', '-y', '-j4', '--path=/foo', 'bah', '-x']
      >>> opt_dict = options.popopts(
      ...   argv,
      ...   _1='once',
      ...   a='all',
      ...   j_=('jobs',int),
      ...   x='-trace_exec',
      ...   y='-dry_run',
      ...   dry_run=None,
      ...   path_=(str, os.path.isabs, 'not an absolute path'),
      ...   verbose=None,
      ... )
      >>> opt_dict
      {'once': True, 'verbose': True, 'dry_run': False, 'jobs': 4, 'path': '/foo'}
      >>> options # doctest: +ELLIPSIS
      DemoOptions(cmd=None, dry_run=False, force=False, quiet=False, runstate_signals=(...), verbose=True, all=False, jobs=4, number=0, once=True, path='/foo', trace_exec=False)

  *`BaseCommandOptions.update(self, **updates)`*:
  Modify the options in place with the mapping `updates`.
  It would be more normal to call the options in a `with` statement
  as shown for `__call__`.

  *`BaseCommandOptions.usage_options_format(headline='Options:', *, _common_opt_specs=None, **opt_specs_kw)`*:
  Return an options paragraph describing `opt_specs_kw`.
  or `''` if `opt_specs_kw` is empty.



  Decorator for `cmd.Cmd` subclass methods
  to supply some basic quality of service.

  This decorator:
  - wraps the function call in a `cs.pfx.Pfx` for context
  - intercepts `getopt.GetoptError`s, issues a `warning`
    and runs `self.do_help` with the method name,
    then returns `None`
  - intercepts other `Exception`s,
    issues an `exception` log message
    and returns `None`

  The intended use is to decorate `cmd.Cmd` `do_`* methods:

      from cmd import Cmd
      from cs.cmdutils import docmd
      ...
      class MyCmd(Cmd):
          @docmd
          def do_something(...):
              ... do something ...



  A class to support parsing an option value.

  *`OptionSpec.__post_init__(self)`*:
  Infer `field_name` and `help_text` if unspecified.

  *`OptionSpec.add_argument(self, parser, options=None)`*:
  Add this option to an `argparser`-style option parser.
  The optional `options` parameter may be used to supply an
  `Options` instance to provide a default value.

  *`OptionSpec.from_opt_kw(opt_k: str, specs: Union[str, List, Tuple, NoneType])`*:
  Factory to produce an `OptionSpec` from a `(key,specs)` 2-tuple
  as from the `items()` from a `popopts()` call.

  The `specs` is normally a list or tuple, but a bare string
  will be promoted to a 1-element list containing the string.

  The elements of `specs` are considered in order for:
  - an identifier specifying the `arg_name`,
    optionally prepended with a dash to indicate an inverted option
  - a help text about the option
  - a callable to parse the option string value to obtain the actual value
  - a callable to validate the option value
  - a message for use when validation fails

  *`OptionSpec.getopt_long`*:
  The option specification for a getopt long option.
  Return `None` if `self.opt_name` is only 1 character.

  *`OptionSpec.getopt_opt`*:
  The `opt` we expect from `opt,val=getopt(argv,...)`.

  *`OptionSpec.getopt_short`*:
  The option specification for a getopt short option.
  Return `''` if `self.opt_name` is longer than 1 character.

  *`OptionSpec.help_text_from_field_name(field_name)`*:
  Compute an inferred `help_text` from an option `field_name`.

  *`OptionSpec.needs_arg`*:
  Whether we expect an argument: we have a `self.arg_name`.

  *`OptionSpec.option_terse(self)`*:
  Return the `"-x"` or `"--name"` option string (with the arg name if expected).

  *`OptionSpec.option_usage(self)`*:
  A 2 line usage entry for this option.
  Example:

      -j jobs
        Job limit.

  *`OptionSpec.parse_value(self, value)`*:
  Parse `value` according to the spec.
  Raises `GetoptError` for invalid values.



  A decorator to parse command line options from a `cmd_`*method*'s `argv`
  and update `self.options`. This also updates the method's usage message.

  Example:

      @popopts(x=('trace', 'Trace execution.'))
      def cmd_do_something(self, argv):
          """ Usage: {cmd} [-x] blah blah
                Do some thing to blah.
          """

  This arranges for cmd_do_something to call `self.options.popopts(argv)`,
  and updates the usage message with an "Options:" paragraph.

  This avoids needing to manually enumerate the options in the
  docstring usage and avoids explicitly calling `self.options.popopts`
  inside the method.



  Call `print()` if `options.verbose` and not `options.quiet`.



  Extract a `"Usage:"`paragraph from a docstring
  and return a 3-tuple of `(preusage,usage,postusage)`.

  If the usage paragraph is not present `''` is returned as
  the middle comonpent, otherwise it is the unindented usage
  without the leading `"Usage:"`.



  An implementation for a subcommand.

  *`SubCommand.__call__(self, argv: List[str])`*:
  Run the subcommand.

  Parameters:
  * `argv`: the command line arguments after the subcommand name

  *`SubCommand.get_cmd(self) -> str`*:
  Return the `cmd` string for this `SubCommand`,
  derived from the subcommand's method name or class name
  if `self.cmd` is not set.

  *`SubCommand.get_subcmds(self)`*:
  Return the names of `self.method`'s subcommands in lexical order.

  *`SubCommand.get_subcommands(self)`*:
  Return `self.method`'s mapping of subcommand name to `SubCommand`.

  *`SubCommand.get_usage_format(self, show_common=False) -> str`*:
  Return the usage format string for this subcommand.
  *Note*: no leading "Usage:" prefix.
  If `show_common` is true, include the `Common options:` paragraph.

  *`SubCommand.get_usage_keywords(self, *, cmd: Optional[str] = None, usage_mapping: Optional[Mapping] = None) -> str`*:
  Return a mapping to be used when formatting the usage format string.

  This is an elaborate `ChainMap` of:
  - the optional `cmd` or `self.get_cmd()`
  - the optional `usage_mapping`
  - `self.usage_mapping`
  - `self.method.USAGE_KEYWORDS` if present
  - the attributes of `self.command`
  - the attributes of `type(self.command)`
  - the attributes of the module for `type(self.command)`

  *`SubCommand.has_subcommands`*:
  Whether this `SubCommand`'s `.method` has subcommands.

  *`SubCommand.instance`*:
  An instance of the class for `self.method`.

  *`SubCommand.short_subusages(self, subcmds: List[str])`*:
  Return a list of tabulated one line subcommand summaries.

  *`SubCommand.usage_text(self, *, cmd=None, short: bool, recurse: bool = False, show_common: bool = False, show_subcmds: Union[bool, str, List[str], NoneType] = None, usage_mapping: Optional[Mapping] = None, seen_subcommands: Optional[Mapping] = None) -> str`*:
  Return the filled out usage text for this subcommand.



  Call `print()` if `options.verbose`.
  This is a compatibility shim for `qvprint()` with `quiet=False`.





  *Release 20241206*:
  * New @popopts decorator to run self.options.popopts and fill out the usage message.
  * Add "-e ssh-command" and the .ssh_exe attribute to BaseCommandOptions.

  *Release 20241122.2*:
  * OptionSpec.getopt_long: fix construction of getopt long option specifications, lost during the recent refactor.
  * Two other minor fixes.

  *Release 20241122.1*:
  * SubCommand.usage_format_parts: backport type annotation for older Pythons.
  * BaseCommand.extract_usage: update parameters for usage_text().

  *Release 20241122*:
  * BaseCommand.cmd_help: new -l (long), -r (recurse) and -s (short, not long) options.
  * A big rework of the help/usage output; still one rough edge left.
  * BaseCommandOptions.popopts: infill with False instead of None for missing boolean options.
  * SubCommand: a few new methods and properties to expose various operations.
  * BaseCommand._prerun_setup: revert SUBCOMMAND_ARGV_DEFAULT to None, make a missing subcommand run "help -s" when there's no SUBCOMMAND_ARGV_DEFAULT.

  *Release 20241119*:
  * SubCommand.usage_text: small improvements and a fix.
  * New BaseCommand.SubCommandClass attribute for the SubCommand subclass, supports using additional SubCommand subclasses for eg cs.djutils.

  *Release 20241117*:
  * _COMMON_OPT_SPECS: include a --dry-run option, alter for -n.
  * SubCommand.usage_text: one line listing for common subcommands.

  *Release 20241110*:
  * Various fixes and enhancements to the usage text generation.
  * BaseCommandOptions.COMMON_OPT_SPECS stuff, including inheritance and presentation in usage.
  * BaseCommand.run: if self.run_context() yields not None, exit immediately with that value.
  * Rationalise and improve OptionSpec.
  * Supplant vprint with qvprint which honours quiet and verbose, provide vprint as a shim with a hardwired quiet=False.
  * Make cmd_info the default subcommand and make some improvements.
  * BaseCommandOptions: new fields_as_dict method, update copy() to start with fields_as_dict, overlay as_dict, overlay updates.
  * OptionSpec: new add_argument(parser[,options]) method to add this option to an argparse style option parser (again, for the upcoming cs.djutils module).
  * A heap of small things.

  *Release 20241007*:
  * BaseCommand: drop __init_subclass__, was doing too much to early.
  * BaseCommand.__init__: no cmd and argv[0] ends with ".py" infer cmd from the class name.
  * Other minor updates.

  *Release 20241005*:
  * BaseCommand: provide an options.runstate to allow the command to override the default (almost never happens).
  * BaseCommand.popopts: fix reference to OptionSpec class.
  * BaseCommand.run: put the try/except:CancellationError outside the self.run_context context manager call, as it can directly raise CancellationError.
  * New vprint() function calling print() if options.verbose.

  *Release 20240709*:
  BaseCommand: support putting the top level Usage in the class docstring instead of as .USAGE_FORMAT, append full usage to the class docstring in `BaseCommand.__init_subclass__`.

  *Release 20240630*:
  * BaseCommand: make SUBCOMMAND_ARGV_DEFAULT be 'shell' for an interactive prompt, still a little unsure how sensible this is, aiming at the very cool submode stuff from the Cisco switch config command line.
  * BaseCommandOptions: new as_dict() method.
  * New SubCommand.usage_text() to compose the full usage text for this SubCommand.
  * Many small improvements and internal refactors.

  *Release 20240519*:
  BaseCommand.run_context: attach the runstate to the options.

  *Release 20240422*:
  * BaseCommandOptions.popopts: return the dict from BaseCommand.popopts().
  * BaseCommand.apply_preargv: apply the default options supported by self.options.
  * BaseCommandOptions.update(mapping) method, useful for dropping subcommand-specific defaults onto the options ahead of the local popopts() call.

  *Release 20240412*:
  * BaseCommand.run_context: do not store .upd and .runstate on the options (it confuses options in subcommands and we have @uses_runstate and @uses_upd forthis anyway these days).
  * BaseCommand.run_context: catch SIGQUIT, present the default handler as BaseCommand.handle_signal.

  *Release 20240316*:
  * New @uses_cmd_options decorator to provide an "options" parameter being the prevailing BaseCommandOptions instance.
  * BaseCommandOptions.popopts: get common options from BaseCommandOptions.COMMON_OPT_SPECS.

  *Release 20240211*:
  * Include the first sentence of the subcommand description in the short help.
  * BaseCommandOptions: move the runstate_signals into this directly.
  * BaseCommand: move the run() options stuff into run_context() and have it work on a copy of the original options.
  * BaseCommandCmd: implement get_names(), provide docstrings for the do_* attributes, thus help.
  * BaseCommand.run_context: make runstate and upd keyword only parameters.

  *Release 20240201*:
  * BaseCommand.run: catch CancellationError and return 1.
  * BaseCommandCmd.__getattr__: recognise EOF, exit and quit to terminate the cmdloop.

  *Release 20231129*:
  BaseCommandOptions: define a runstate field.

  *Release 20230703*:
  Small internal changes.

  *Release 20230612*:
  * BaseCommand.cmdloop: fix intro parameter.
  * Other small fixes.

  *Release 20230407*:
  * BaseCommand: use @uses_runstate when preparing the command, store as self.options.runstate.
  * Make BaseCommandOptions a data class.
  * Drop any pretence at python 2 support, we're long past that.
  * BaseCommand: new cmdloop method to run a cmd.Cmd instance to run subcommand interactively.
  * BaseCommand: rename shell to repl, add cmd_shell to call cmdloop().
  * Drop BaseCommand.apply_defaults in favour of the Options dataclass.
  * BaseCommand: do setup_logging before initiating the Options instance.

  *Release 20230212*:
  * BaseCommand.run_context: update RunState support.
  * BaseCommand.run_context: always be having an self.options.upd.

  *Release 20230211*:
  BaseCommand: new shell() method to present an interactive Python prompt for use by subclasses cmd_shell method if desired.

  *Release 20221228*:
  Move a lot of the context logic from BaseCommand.run to BaseCommand.run_context, which now must be correctly overridden in subclasses.

  *Release 20220918*:
  * BaseCommand.run_context: expand default signals to include SIGHUP, expose as BaseCommand.DEFAULT_SIGNALS.
  * BaseCommand.run: pass in the subclass handle_signal method if present.

  *Release 20220626*:
  * BaseCommand.poparg: fix positional argument handling.
  * BaseCommand.poparg: new unpop_on_error=False parameter to support pushing a bad argument back onto the front of the argument list.

  *Release 20220606*:
  BaseCommand.run: remove the Upd bodge, too annoying, now fixed in cs.upd I believe.

  *Release 20220605*:
  * BaseCommand: new popopts(argv,...) compact getopt wrapper.
  * BaseCommand: new poparg(argv,...) compact validating argument consumer.
  * BaseCommand: drop run_argv, provided no utility.
  * BaseCommand.run: get the RunState signal list from self.options.runstate_signals.
  * BaseCommand.apply_opts: support multiple individual options raising GetoptError, as I hate commands which abort at the first bad option.
  * Assorted other small things.

  *Release 20220429*:
  * BaseCommand: fold dots in argv[0] into underscores, supports subcommands like "setup.py".
  * BaseCommand: new popargv(argv[,help_text[,parse[,validate[,unvalidated_message]]]]) helper class method.
  * BaseCommand: accept dashed-form of the underscored_form subcommand name.
  * BaseCommand: new self.options.runstate_signals=SIGINT,SIGTERM specifying singals to catch-and-cancel, shuffle run() context managers.

  *Release 20220318*:
  BaseCommand.__init__: handle main() method in the New Scheme.

  *Release 20220315*:
  _BaseSubCommand.__init__: hook in the class USAGE_KEYWORDS for methods.

  *Release 20220311*:
  BaseCommand: big refactor of subcommand internals and make the "cmd_foo=FooCommand" implementation work properly.

  *Release 20211208*:
  BaseCommand: better handle an unknown subcommand.

  *Release 20210927*:
  * Usage: show only the per subcommand usage for in-subcommand GetoptError.
  * Usage: show terse usage when the subcommand cannot be recognised.
  * Usage: support bare -h, -help, --help.

  *Release 20210913*:
  New BaseCommand.apply_preargv method to gather special arguments before subcommands.

  *Release 20210906*:
  * BaseCommand.cmd_help: bugfix obsolete parameter list.
  * BaseCommand.SUBCOMMAND_ARGV_DEFAULT: support a single str value, turn into list.

  *Release 20210809*:
  Bugfix BaseCommand.cmd_help for modern API.

  *Release 20210731*:
  * BaseCommand.run: apply optional keyword arguments to self.options during the run.
  * Look for self.SUBCOMMAND_ARGV_DEFAULT if no subcommand is supplied.
  * Bugfix case for "main" method and no "cmd_*" methods.
  * Bugfix BaseCommand.cmd_help.

  *Release 20210420*:
  * BaseCommand.getopt_error_handler: replace error print() with warning().
  * Docstring improvements.

  *Release 20210407.1*:
  BaseCommand: bugfix for __init_subclass__ docstring update.

  *Release 20210407*:
  * BaseCommand.__init_subclass__: behave sanely if the subclass has no initial __doc__.
  * BaseCommand: new .run_argv convenience method, obviates the "def main" boilerplate.

  *Release 20210404*:
  BaseCommand subclasses: automatically add the main usage message to the subclass docstring.

  *Release 20210306*:
  * BREAKING CHANGE: rework BaseCommand as a more normal class instantiated with argv and with most methods being instance methods, getting the former `options` parameter from self.options.
  * BaseCommand: provide default `apply_opt` and `apply_opts` methods; subclasses will generally just override the former.

  *Release 20210123*:
  BaseCommand: propagate the format mapping (cmd, USAGE_KEYWORDS) to the subusage generation.

  *Release 20201102*:
  * BaseCommand.cmd_help: supply usage only for "all commands", full docstring for specified commands.
  * BaseCommand: honour presupplied options.log_level.
  * BaseCommand.usage_text: handle missing USAGE_FORMAT better.
  * BaseCommand.run: provide options.upd.
  * BaseCommand subclasses may now override BaseCommand.OPTIONS_CLASS (default SimpleNamespace) in order to provide convenience methods on the options.
  * BaseCommand.run: separate variable for subcmd with dash translated to underscore to match method names.
  * Minor fixes.

  *Release 20200615*:
  BaseCommand.usage_text: do not mention the "help" command if it is the only subcommand (it won't be available if there are no other subcommands).

  *Release 20200521.1*:
  Fix DISTINFO.install_requires.

  *Release 20200521*:
  * BaseCommand.run: support using BaseCommand subclasses as cmd_* names to make it easy to nest BaseCommands.
  * BaseCommand: new hack_postopts_argv method called after parsing the main command line options, for inferring subcommands or the like.
  * BaseCommand: extract "Usage:" paragraphs from subcommand method docstrings to build the main usage message.
  * BaseCommand: new cmd_help default command.
  * Assorted bugfixes and small improvements.

  *Release 20200318*:
  * BaseCommand.run: make argv optional, get additional usage keywords from self.USAGE_KEYWORDS.
  * @BaseCommand.add_usage_to_docstring: honour cls.USAGE_KEYWORDS.
  * BaseCommand: do not require GETOPT_SPEC for commands with no defined options.
  * BaseCommand.run: call cs.logutils.setup_logging.

  *Release 20200229*:
  Improve subcommand selection logic, replace StackableValues with stackattrs, drop `cmd` from arguments passed to main/cmd_* methods (present in `options`).

  *Release 20200210*:
  * New BaseCommand.add_usage_to_docstring class method to be called after class setup, to append the usage message to the class docstring.
  * BaseCommand.run: remove spurious Pfx(cmd), as logutils does this for us already.

  *Release 20190729*:
  BaseCommand: support for a USAGE_FORMAT usage message format string and a getopt_error_handler method.

  *Release 20190619.1*:
  Another niggling docstring formatting fix.

  *Release 20190619*:
  Minor documentation updates.

  *Release 20190617.2*:
  Lint.

  *Release 20190617.1*:
  Initial release with @docmd decorator and alpha quality BaseCommand command line assistance class.


pkgId: 62a937d9cedc656e6ad576932d8069e53b09cb566ca4b2139cba4f9a2b9d2b35
name: python-cs-deco
arch: src
version: 20241206
epoch: 0
release: 1
summary: Assorted function decorators.
url: https://pypi.org/project/cs-deco/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-deco-20241206-1.src.rpm
checksum_type: sha256
description:
  Assorted function decorators.

  *Latest release 20241206*:
  @uses_cmd_options: use an object subclass if there is no cs.cmdutils so that we can add attributes to the placeholder options object.



  Include this function's name in its module's `__all__` list.

  Example:

      from cs.deco import ALL

      __all__ = []

      def obscure_function(...):
          ...

      @ALL
      def well_known_function(...):
          ...



  OBSOLETE cached

  Former name for @cachedmethod.



  Decorator to cache the result of an instance or class method
  and keep a revision counter for changes.

  The cached values are stored on the instance (`self`).
  The revision counter supports the `@revised` decorator.

  This decorator may be used in 2 modes.
  Directly:

      @cachedmethod
      def method(self, ...)

  or indirectly:

      @cachedmethod(poll_delay=0.25)
      def method(self, ...)

  Optional keyword arguments:
  * `attr_name`: the basis name for the supporting attributes.
    Default: the name of the method.
  * `poll_delay`: minimum time between polls; after the first
    access, subsequent accesses before the `poll_delay` has elapsed
    will return the cached value.
    Default: `None`, meaning the value never becomes stale.
  * `sig_func`: a signature function, which should be significantly
    cheaper than the method. If the signature is unchanged, the
    cached value will be returned. The signature function
    expects the instance (`self`) as its first parameter.
    Default: `None`, meaning no signature function;
    the first computed value will be kept and never updated.
  * `unset_value`: the value to return before the method has been
    called successfully.
    Default: `None`.

  If the method raises an exception, this will be logged and
  the method will return the previously cached value,
  unless there is not yet a cached value
  in which case the exception will be reraised.

  If the signature function raises an exception
  then a log message is issued and the signature is considered unchanged.

  An example use of this decorator might be to keep a "live"
  configuration data structure, parsed from a configuration
  file which might be modified after the program starts. One
  might provide a signature function which called `os.stat()` on
  the file to check for changes before invoking a full read and
  parse of the file.

  *Note*: use of this decorator requires the `cs.pfx` module.



  A decorator for a context manager function `cmgrfunc`
  which turns it into a decorator for other functions.

  This supports easy implementation of "setup" and "teardown"
  code around other functions without the tedium of defining
  the wrapper function itself. See the examples below.

  The resulting context manager accepts an optional keyword
  parameter `provide_context`, default `False`. If true, the
  context returned from the context manager is provided as the
  first argument to the call to the wrapped function.

  Note that the context manager function `cmgrfunc`
  has _not_ yet been wrapped with `@contextmanager`,
  that is done by `@contextdecorator`.

  This decorator supports both normal functions and generator functions.

  With a normal function the process is:
  * call the context manager with `(func,a,kw,*da,**dkw)`,
    returning `ctxt`,
    where `da` and `dkw` are the positional and keyword parameters
    supplied when the decorator was defined.
  * within the context
    return the value of `func(ctxt,*a,**kw)` if `provide_context` is true
    or the value of `func(*a,**kw)` if not (the default)

  With a generator function the process is:
  * obtain an iterator by calling `func(*a,**kw)`
  * for iterate over the iterator, yielding its results,
    by calling the context manager with `(func,a,kw,**da,**dkw)`,
    around each `next()`
  Note that it is an error to provide a true value for `provide_context`
  if the decorated function is a generator function.

  Some examples follow.

  Trace the call and return of a specific function:

      @contextdecorator
      def tracecall(func, a, kw):
          """ Trace the call and return from some function.
              This can easily be adapted to purposes such as timing a
              function call or logging use.
          """
          print("call %s(*%r,**%r)" % (func, a, kw))
          try:
            yield
          except Exception as e:
            print("exception from %s(*%r,**%r): %s" % (func, a, kw, e))
            raise
          else:
            print("return from %s(*%r,**%r)" % (func, a, kw))

      @tracecall
      def f():
          """ Some function to trace.
          """

      @tracecall(provide_context=True):
      def f(ctxt, *a, **kw):
          """ A function expecting the context object as its first argument,
              ahead of whatever other arguments it would normally require.
          """

  See who is making use of a generator's values,
  when a generator might be invoked in one place and consumed elsewhere:

      from cs.py.stack import caller

      @contextdecorator
      def genuser(genfunc, *a, **kw):
          user = caller(-4)
          print(f"iterate over {genfunc}(*{a!r},**{kw!r}) from {user}")
          yield

      @genuser
      def linesof(filename):
          with open(filename) as f:
              yield from f

      
      lines = linesof(__file__)

      
      for lineno, line in enumerate(lines, 1):
          print("line %d: %d words" % (lineno, len(line.split())))

  Turn on "verbose mode" around a particular function:

      import sys
      import threading
      from cs.context import stackattrs

      class State(threading.local):
          def __init__(self):
              
              self.verbose = sys.stderr.isatty()

      
      state = State()

      @contextdecorator
      def verbose(func):
          with stackattrs(state, verbose=True) as old_attrs:
              if not old_attrs['verbose']:
                  print(f"enabled verbose={state.verbose} for function {func}")
              
              yield old_attrs['verbose']

      
      @verbose
      def func(x, y):
          if state.verbose:
              
              print("x =", x, "y =", y)

      
      
      @verbose(provide_context=True):
      def func2(old_verbose, x, y):
          if state.verbose:
              
              print("old_verbosity =", old_verbose, "x =", x, "y =", y)



  Wrap a simple function as a context manager.

  This was written to support users of `@strable`,
  which requires its `open_func` to return a context manager;
  this turns an arbitrary function into a context manager.

  Example promoting a trivial function:

      >>> f = lambda: 3
      >>> cf = contextual(f)
      >>> with cf() as x: print(x)
      3



  Wrapper for decorator functions to support optional arguments.

  The actual decorator function ends up being called as:

      mydeco(func, *da, **dkw)

  allowing `da` and `dkw` to affect the behaviour of the decorator `mydeco`.

  Examples:

      
      @decorator
      def mydeco(func, *da, arg2=None):
        ... decorate func subject to the values of da and arg2

      
      @mydeco
      def func1(...):
        ...

      @ mydeco called with nondefault arguments
      @mydeco('foo', arg2='bah')
      def func2(...):
        ...



  Decorator to provide factory functions for default parameters.

  This decorator accepts the following keyword parameters:
  * `_strict`: default `False`; if true only replace genuinely
    missing parameters; if false also replace the traditional
    `None` placeholder value
  The remaining keyword parameters are factory functions
  providing the respective default values.

  Atypical one off direct use:

      @default_params(dbconn=open_default_dbconn,debug=lambda:settings.DB_DEBUG_MODE)
      def dbquery(query, *, dbconn):
          dbconn.query(query)

  Typical use as a decorator factory:

      
      uses_ds3 = default_params(ds3client=get_ds3client)

      
      @uses_ds3
      def do_something(.., *, ds3client, ...):
          ... make queries using ds3client ...

  This replaces the standard boilerplate and avoids replicating
  knowledge of the default factory as exhibited in this legacy code:

      def do_something(.., *, ds3client=None, ...):
          if ds3client is None:
              ds3client = get_ds3client()
          ... make queries using ds3client ...



  Decorator to replace a function's docstring with that string
  formatted against the function's module `__dict__`.

  This supports simple formatted docstrings:

      ENVVAR_NAME = 'FUNC_DEFAULT'

      @fmtdoc
      def func():
          """Do something with os.environ[{ENVVAR_NAME}]."""
          print(os.environ[ENVVAR_NAME])

  This gives `func` this docstring:

      Do something with os.environ[FUNC_DEFAULT].

  *Warning*: this decorator is intended for wiring "constants"
  into docstrings, not for dynamic values. Use for other types
  of values should be considered with trepidation.



  Decorator for logging call shims
  which bumps the `stacklevel` keyword argument so that the logging system
  chooses the correct frame to cite in messages.

  Note: has no effect on Python < 3.8 because `stacklevel` only
  appeared in that version.



  Class decorator to make various instance attributes observable.

  Parameters:
  * `property_names`:
    an interable of instance property names to set up as
    observable properties. As a special case a single `str` can
    be supplied if only one attribute is to be observed.
  * `only_unequal`:
    only call the observers if the new property value is not
    equal to the previous proerty value. This requires property
    values to be comparable for inequality.
    Default: `False`, meaning that all updates will be reported.



  A decorator for obsolete functions or classes.

  Use:

      @OBSOLETE
      def func(...):

  or

      @OBSOLETE("new_func_name")
      def func(...):

  This emits a warning log message before calling the decorated function.
  Only one warning is emitted per calling location.



  A mixin class which supports the `@promote` decorator.

  *`Promotable.promote(obj, **from_t_kw)`*:
  Promote `obj` to an instance of `cls` or raise `TypeError`.
  This method supports the `@promote` decorator.

  This base method will call the `from_`*typename*`(obj,**from_t_kw)`
  class factory method if present, where *typename* is
  `obj.__class__.__name__`.

  Subclasses may override this method to promote other types,
  typically:

      @classmethod
      def promote(cls, obj):
          if isinstance(obj, cls):
              return obj
          ... various specific type promotions
          ... not done via a from_typename factory method
          
          return super().promote(obj)

  An typical `from_`*typename*` factory method:

      class Foo(Promotable):

          def __init__(self, dbkey, dbrow):
              self.key = dbkey
              self.row_data = row

          @classmethod
          def from_str(cls, s : str):
              """Accept a database key string, return a `Foo` instance."""
              row = db_lookup(s)
              return cls(s, row)

  This supports using `@promote` on functions with `Foo` instances:

      @promote
      def do_it(foo : Foo):
          ... work with foo ...

  but calling it as:

      do_it("foo_key_value")



  A decorator to promote argument values automatically in annotated functions.

  If the annotation is `Optional[some_type]` or `Union[some_type,None]`
  then the promotion will be to `some_type` but a value of `None`
  will be passed through unchanged.

  The decorator accepts optional parameters:
  * `params`: if supplied, only parameters in this list will
    be promoted
  * `types`: if supplied, only types in this list will be
    considered for promotion

  For any parameter with a type annotation, if that type has a
  `.promote(value)` class method and the function is called with a
  value not of the type of the annotation, the `.promote` method
  will be called to promote the value to the expected type.

  Note that the `Promotable` mixin provides a `.promote()`
  method which promotes `obj` to the class if the class has a
  factory class method `from_`*typename*`(obj)` where *typename*
  is `obj.__class__.__name__`.
  A common case for me is lexical objects which have a `from_str(str)`
  factory to produce an instance from its textual form.

  Additionally, if the `.promote(value)` class method raises a `TypeError`
  and `value` has a `.as_`*typename* attribute
  (where *typename* is the name of the type annotation),
  if that attribute is an instance method of `value`
  then promotion will be attempted by calling `value.as_`*typename*`()`
  otherwise the attribute will be used directly
  on the presumption that it is a property.

  A typical `promote(cls, obj)` method looks like this:

      @classmethod
      def promote(cls, obj):
          if isinstance(obj, cls):
              return obj
          ... recognise various types ...
          ... and return a suitable instance of cls ...
          raise TypeError(
              "%s.promote: cannot promote %s:%r",
              cls.__name__, obj.__class__.__name__, obj)

  Example:

      >>> from cs.timeseries import Epoch
      >>> from typeguard import typechecked
      >>>
      >>> @promote
      ... @typechecked
      ... def f(data, epoch:Epoch=None):
      ...     print("epoch =", type(epoch), epoch)
      ...
      >>> f([1,2,3], epoch=12.0)
      epoch = <class 'cs.timeseries.Epoch'> Epoch(start=0, step=12)

  Example using a class with an `as_P` instance method:

      >>> class P:
      ...   def __init__(self, x):
      ...     self.x = x
      ...   @classmethod
      ...   def promote(cls, obj):
      ...     raise TypeError("dummy promote method")
      ...
      >>> class C:
      ...   def __init__(self, n):
      ...     self.n = n
      ...   def as_P(self):
      ...     return P(self.n + 1)
      ...
      >>> @promote
      ... def p(p: P):
      ...   print("P =", type(p), p.x)
      ...
      >>> c = C(1)
      >>> p(c)
      P = <class 'cs.deco.P'> 2

  *Note*: one issue with this is due to the conflict in name
  between this decorator and the method it looks for in a class.
  The `promote` _method_ must appear after any methods in the
  class which are decorated with `@promote`, otherwise the
  `promote` method supplants the name `promote` making it
  unavailable as the decorator.
  I usually just make `.promote` the last method.

  Failing example:

      class Foo:
          @classmethod
          def promote(cls, obj):
              ... return promoted obj ...
          @promote
          def method(self, param:Type, ...):
              ...

  Working example:

      class Foo:
          @promote
          def method(self, param:Type, ...):
              ...
          
          @classmethod
          def promote(cls, obj):
              ... return promoted obj ...



  Decorator for functions which may accept a `str`
  instead of their core type.

  Parameters:
  * `func`: the function to decorate
  * `open_func`: the "open" factory to produce the core type
    if a string is provided;
    the default is the builtin "open" function.
    The returned value should be a context manager.
    Simpler functions can be decorated with `@contextual`
    to turn them into context managers if need be.

  The usual (and default) example is a function to process an
  open file, designed to be handed a file object but which may
  be called with a filename. If the first argument is a `str`
  then that file is opened and the function called with the
  open file.

  Examples:

      @strable
      def count_lines(f):
        return len(line for line in f)

      class Recording:
        "Class representing a video recording."
        ...
      @strable(open_func=Recording)
      def process_video(r):
        ... do stuff with `r` as a Recording instance ...

  *Note*: use of this decorator requires the `cs.pfx` module.



  A decorator to provide default keyword arguments
  from the prevailing `cs.cmdutils.BaseCommandOptions`
  if available, otherwise from `option_defaults`.

  This exists to provide plumbing free access to options set
  up by a command line invocation using `cs.cmdutils.BaseCommand`.

  If no `option_defaults` are provided, a single `options`
  keyword argument is provided which is the prevailing
  `BaseCommand.Options` instance.

  The decorator accepts two optional "private" keyword arguments
  commencing with underscores:
  * `_strict`: default `False`; if true then an `option_defaults`
    will only be applied if the argument is _missing_ from the
    function arguments, otherwise it will be applied if the
    argument is missing or `None`
  * `_options_param_name`: default `'options'`; this is the
    name of the single `options` keyword argument which will be
    supplied if there are no `option_defaults`

  Examples:

      @uses_cmd_options(doit=True, quiet=False)
      def func(x, *, doit, quiet, **kw):
          if not quiet:
              print("something", x, kw)
          if doit:
             ... do the thing ...
          ... etc ...

      @uses_cmd_options()
      def func(x, *, options, **kw):
          if not options.quiet:
              print("something", x, kw)
          if options.doit:
             ... do the thing ...
          ... etc ...



  `func` is not supplied, collect the arguments supplied and return a
  decorator which takes the subsequent callable and returns
  `deco(func, *da, **kw)`.



  `func` is not supplied, collect the arguments supplied and return a
  decorator which takes the subsequent callable and returns
  `deco(func, *da, **kw)`.



  `func` is not supplied, collect the arguments supplied and return a
  decorator which takes the subsequent callable and returns
  `deco(func, *da, **kw)`.



  `func` is not supplied, collect the arguments supplied and return a
  decorator which takes the subsequent callable and returns
  `deco(func, *da, **kw)`.





  *Release 20241206*:
  @uses_cmd_options: use an object subclass if there is no cs.cmdutils so that we can add attributes to the placeholder options object.

  *Release 20241109*:
  * Add @uses_force to @uses_doit, @uses_quiet, @uses_verbose decorator set.
  * Fold @uses_cmd_option into @uses_cmd_options, provide "options" if no option_defaults.
  * @promote: accept optional keyword arguments, plumb to the from_typename factory method.

  *Release 20241007*:
  @uses_cmd_option: bugfix the infill of the defaults.

  *Release 20241003*:
  New @uses_cmd_option and @uses_doit, @uses_quiet and @uses_verbose based on it.

  *Release 20240709*:
  Bugfix @default_params: assemble the new signature with a complete set of parameters instead of only the defaulted ones.

  *Release 20240630*:
  * @default_params: apply modified signature parameters one at a time, as applying them in a single list seems to trigger some ordering checks.
  * @promote: pass the original Parameter through to the wrapper so that we can use .default if needed, have the wrapper test for None last, so that we can promote None to something with a .from_NoneType method, make the check for the default value more specific.

  *Release 20240412*:
  @decorator: apply the decorated function name to the metadecorator.

  *Release 20240326*:
  default_params: update wrapper signature to mark the defaulted params as optional.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240314.1*:
  New release with corrected install path.

  *Release 20240314*:
  Tiny doc update.

  *Release 20240303*:
  Promotable.promote: do not just handle str, handle anything with a from_*typename* factory method.

  *Release 20240211*:
  Promotable: no longer abstract, provide a default promote() method which tries cls.from_str for str.

  *Release 20231129*:
  @cachedmethod: ghastly hack for the revision attribute to accomodate objects which return None for missing attributes.

  *Release 20230331*:
  @promote: pass None through for Optional parameters.

  *Release 20230212*:
  New Promotable abstract class mixin, which requires the creation of a .promote(cls,obj)->cls class method.

  *Release 20230210*:
  @promote: add support for .as_TypeName() instance method on the source object or a .as_TypeName property/attribut.

  *Release 20221214*:
  @decorator: use functools.update_wrapper to propagate the decorated function's attributes to the wrapper (still legacy code for Python prior to 3.2).

  *Release 20221207*:
  Small updates.

  *Release 20221106.1*:
  @promote: support Optional[sometype] parameters.

  *Release 20221106*:
  New @promote decorator to autopromote parameter values according to their type annotation.

  *Release 20220918.1*:
  @default_param: append parameter descriptions to the docstring of the decorated function, bugfix __name__ setting.

  *Release 20220918*:
  * @OBSOLETE: tweak message format.
  * @default_params: docstring: simplify the example and improve the explaination.
  * @default_params: set the __name__ of the wrapper function.

  *Release 20220905*:
  * New @ALL decorator to include a function in __all__.
  * New @default_params decorator for making decorators which provide default argument values from callables.

  *Release 20220805*:
  @OBSOLETE: small improvements.

  *Release 20220327*:
  Some fixes for @cachedmethod.

  *Release 20220311*:
  @cachedmethod: change the meaning of poll_delay=None to mean "never goes stale" as I had thought it already did.

  *Release 20220227*:
  @cachedmethod: more paranoid access to the revision attribute.

  *Release 20210823*:
  @decorator: preserve the __name__ of the wrapped function.

  *Release 20210123*:
  Syntax backport for older Pythons.

  *Release 20201202*:
  @decorator: tweak test for callable(da[0]) to accord with the docstring.

  *Release 20201025*:
  New @contextdecorator decorator for context managers to turn them into setup/teardown decorators.

  *Release 20201020*:
  * @cachedmethod: bugfix cache logic.
  * @strable: support generator functions.

  *Release 20200725*:
  Overdue upgrade of @decorator to support combining the function and decorator args in one call.

  *Release 20200517.2*:
  Minor upgrade to @OBSOLETE.

  *Release 20200517.1*:
  Tweak @OBSOLETE and @cached (obsolete name for @cachedmethod).

  *Release 20200517*:
  Get warning() from cs.gimmicks.

  *Release 20200417*:
  * @decorator: do not override __doc__ on the decorated function, just provide default.
  * New @logging_wrapper which bumps the `stacklevel` parameter in Python 3.8 and above so that shims recite the correct caller.

  *Release 20200318.1*:
  New @OBSOLETE to issue a warning on a call to an obsolete function, like an improved @cs.logutils.OBSOLETE (which needs to retire).

  *Release 20200318*:
  @cachedmethod: tighten up the "is the value changed" try/except.

  *Release 20191012*:
  * New @contextual decorator to turn a simple function into a context manager.
  * @strable: mention context manager requirement and @contextual as workaround.

  *Release 20191006*:
  Rename @cached to @cachedmethod, leave compatible @cached behind which issues a warning (will be removed in a future release).

  *Release 20191004*:
  Avoid circular import with cs.pfx by removing requirement and doing the import later if needed.

  *Release 20190905*:
  Bugfix @deco: it turns out that you may not set the .__module__ attribute on a property object.

  *Release 20190830.2*:
  Make some getattr calls robust.

  *Release 20190830.1*:
  @decorator: set the __module__ of the wrapper.

  *Release 20190830*:
  @decorator: set the __module__ of the wrapper from the decorated target, aids cs.distinf.

  *Release 20190729*:
  @cached: sidestep uninitialised value.

  *Release 20190601.1*:
  @strable: fix the example in the docstring.

  *Release 20190601*:
  * Bugfix @decorator to correctly propagate the docstring of the subdecorator.
  * Improve other docstrings.

  *Release 20190526*:
  @decorator: add support for positional arguments and rewrite - simpler and clearer.

  *Release 20190512*:
  @fmtdoc: add caveat against misuse of this decorator.

  *Release 20190404*:
  New @fmtdoc decorator to format a function's doctsring against its module's globals.

  *Release 20190403*:
  * @cached: bugfix: avoid using unset sig_func value on first pass.
  * @observable_class: further tweaks.

  *Release 20190322.1*:
  @observable_class: bugfix __init__ wrapper function.

  *Release 20190322*:
  * New class decorator @observable_class.
  * Bugfix import of "warning".

  *Release 20190309*:
  @cached: improve the exception handling.

  *Release 20190307.2*:
  Fix docstring typo.

  *Release 20190307.1*:
  Bugfix @decorator: final plumbing step for decorated decorator.

  *Release 20190307*:
  * @decorator: drop unused arguments, they get used by the returned decorator.
  * Rework the @cached logic.

  *Release 20190220*:
  * Bugfix @decorator decorator, do not decorate twice.
  * Have a cut at inheriting the decorated function's docstring.

  *Release 20181227*:
  * New decoartor @strable for function which may accept a str instead of their primary type.
  * Improvements to @cached.

  *Release 20171231*:
  Initial PyPI release.


pkgId: fe02af41efc0dc5bbb12f752b8757d831a7ca79f281407a8ab501f86b8eb952d
name: python-cs-ffmpegutils
arch: src
version: 20241122
epoch: 0
release: 1
summary: Convenience facilities for using FFmpeg (ffmpeg.org), with invocation via `ffmpeg-python`.
url: https://pypi.org/project/cs-ffmpegutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-ffmpegutils-20241122-1.src.rpm
checksum_type: sha256
description:
  Convenience facilities for using FFmpeg (ffmpeg.org),
  with invocation via `ffmpeg-python`.

  *Latest release 20241122*:
  Honour the $FFMPEG_DOCKER_IMAGE environment variable.



  ConversionSource(src, srcfmt, start_s, end_s)

  *`ConversionSource.end_s`*:
  Alias for field number 3

  *`ConversionSource.src`*:
  Alias for field number 0

  *`ConversionSource.srcfmt`*:
  Alias for field number 1

  *`ConversionSource.start_s`*:
  Alias for field number 2



  Transcode video to `dstpath` in FFMPEG compatible `dstfmt`.



  Invoke `ffmpeg` using docker.



  A representation of an `ffmpeg` input source.

  *`FFmpegSource.add_as_input(self, ff)`*:
  Add as an input to `ff`.
  Return `None` if `self.source` is a pathname,
  otherwise return the file descriptor of the data source.

  Note: because we rely on `ff.input('pipe:')` for nonpathnames,
  you can only use a nonpathname `FFmpegSource` for one of the inputs.
  This is not checked.

  *`FFmpegSource.promote(source)`*:
  Promote `source` to an `FFmpegSource`.



  Run `ffprobe -print_format json` on `input_file`,
  return format, stream, program and chapter information
  as an `AttrableMapping` (a `dict` subclass).



  The `ffm[peg-docker` command line implementation.



  Object containing fields which may be supplied to ffmpeg's -metadata option.

  *`MetaData.__init__(self, format, **kw)`*:
  pylint: disable=redefined-builtin

  *`MetaData.options(self)`*:
  Compute the FFmpeg -metadata option strings and return as a list.





  *Release 20241122*:
  Honour the $FFMPEG_DOCKER_IMAGE environment variable.

  *Release 20240519*:
  ffmpeg_docker: set DockerRun.output_hostdir from the output file dirname.

  *Release 20240316.1*:
  DISTINFO fix.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240201*:
  * New $FFMPEG_EXE envvar.
  * convert: use $FFMPEG_EXE, return the ffmpeg argv.
  * convert: include the media type in DEFAULT_CONVERSIONS, refactor the choice of codec conversion.
  * convert: supply dummy results if doit is false - could do with some finesse.
  * ffmpeg_docker: use DockerRun.{add_input,add_output}.
  * New main_ffmpeg_docker to support the ffmpeg-docker command, add ffmpeg-docker to DISTINFO scripts.

  *Release 20231202*:
  Initial PyPI release.


pkgId: 84099ee5b3a23d23841298ae312583641e61f4cbbe58a89dd4d4a6bd2b81de52
name: python-cs-fileutils
arch: src
version: 20241122
epoch: 0
release: 1
summary: My grab bag of convenience functions for files and filenames/pathnames.
url: https://pypi.org/project/cs-fileutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-fileutils-20241122-1.src.rpm
checksum_type: sha256
description:
  My grab bag of convenience functions for files and filenames/pathnames.

  *Latest release 20241122*:
  New atomic_copy2 function which is an atomic version of shutil.copy2.



  Return the absolute path of `path` with respect to `from_file`,
  as one might do for an include file.



  Call `shutil.copy2` to copy `srcpath` to `dstpath` via a
  temporary file using `atomic_filename`.
  This differs from `shutil.copy2` in 2 ways:
  - it is an error if `dstpath` already exists unless you supply
    `exists_ok=True`
  - the new copy appears atomicly when the copy is complete
    instead of be visible partially complete during the copy
  The `follow_symlinks=True` parameter is passed to `shutil.copy2`.
  Other keyword parameters are passed to `atomic_filename`.



  A context manager to create `filename` atomicly on completion.
  This yields a `NamedTemporaryFile` to use to create the file contents.
  On completion the temporary file is renamed to the target name `filename`.

  If the caller decides to _not_ create the target they may remove the
  temporary file. This is not considered an error.

  Parameters:
  * `filename`: the file name to create
  * `exists_ok`: default `False`;
    if true it not an error if `filename` already exists
  * `placeholder`: create a placeholder file at `filename`
    while the real contents are written to the temporary file
  * `dir`: passed to `NamedTemporaryFile`, specifies the directory
    to hold the temporary file; the default is `dirname(filename)`
    to ensure the rename is atomic
  * `prefix`: passed to `NamedTemporaryFile`, specifies a prefix
    for the temporary file; the default is a dot (`'.'`) plus the prefix
    from `splitext(basename(filename))`
  * `suffix`: passed to `NamedTemporaryFile`, specifies a suffix
    for the temporary file; the default is the extension obtained
    from `splitext(basename(filename))`
  * `rename_func`: a callable accepting `(tempname,filename)`
    used to rename the temporary file to the final name; the
    default is `os.rename` and this parametr exists to accept
    something such as `FSTags.move`
  Other keyword arguments are passed to the `NamedTemporaryFile` constructor.

  Example:

      >>> import os
      >>> from os.path import exists as existspath
      >>> fn = 'test_atomic_filename'
      >>> with atomic_filename(fn, mode='w') as f:
      ...     assert not existspath(fn)
      ...     print('foo', file=f)
      ...     assert not existspath(fn)
      ...
      >>> assert existspath(fn)
      >>> assert open(fn).read() == 'foo\n'
      >>> os.remove(fn)



  A RawIOBase duck type
  which uses a backing file for initial data
  and writes new data to a front scratch file.

  *`BackedFile.__init__(self, back_file, dirpath=None)`*:
  Initialise the BackedFile using `back_file` for the backing data.

  *`BackedFile.__enter__(self)`*:
  BackedFile instances offer a context manager that take the lock,
  allowing synchronous use of the file
  without implementing a suite of special methods like pread/pwrite.

  *`BackedFile.close(self)`*:
  Close the BackedFile.
  Flush contents. Close the front_file if necessary.

  *`BackedFile.datafrom(self, offset)`*:
  Generator yielding natural chunks from the file commencing at offset.

  *`BackedFile.seek(self, pos, whence=0)`*:
  Adjust the current file pointer offset.

  *`BackedFile.switch_back_file(self, new_back_file)`*:
  Switch out one back file for another. Return the old back file.

  *`BackedFile.tell(self)`*:
  Report the current file pointer offset.

  *`BackedFile.write(self, b)`*:
  Write data to the front_file.



  Mixin for testing subclasses of BackedFile.
  Tests self.backed_fp.

  *`BackedFile_TestMethods.test_BackedFile(self)`*:
  Test function for a BackedFile to use in unit test suites.



  Deliver the iterable of bytes `bss` as a readable file descriptor.
        Return the file descriptor.
        Any keyword arguments are passed to `CornuCopyBuffer.as_fd`.

        Example:

             
             
             rfd = byteses_as_fd([(passphrase + '
  ').encode()])



  Return the common path prefix of the `paths`.

  Note that the common prefix of `'/a/b/c1'` and `'/a/b/c2'`
  is `'/a/b/'`, _not_ `'/a/b/c'`.

  Callers may find it useful to preadjust the supplied paths
  with `normpath`, `abspath` or `realpath` from `os.path`;
  see the `os.path` documentation for the various caveats
  which go with those functions.

  Examples:

      >>> # the obvious
      >>> common_path_prefix('', '')
      ''
      >>> common_path_prefix('/', '/')
      '/'
      >>> common_path_prefix('a', 'a')
      'a'
      >>> common_path_prefix('a', 'b')
      ''
      >>> # nonempty directory path prefixes end in os.sep
      >>> common_path_prefix('/', '/a')
      '/'
      >>> # identical paths include the final basename
      >>> common_path_prefix('p/a', 'p/a')
      'p/a'
      >>> # the comparison does not normalise paths
      >>> common_path_prefix('p//a', 'p//a')
      'p//a'
      >>> common_path_prefix('p//a', 'p//b')
      'p//'
      >>> common_path_prefix('p//a', 'p/a')
      'p/'
      >>> common_path_prefix('p/a', 'p/b')
      'p/'
      >>> # the comparison strips complete unequal path components
      >>> common_path_prefix('p/a1', 'p/a2')
      'p/'
      >>> common_path_prefix('p/a/b1', 'p/a/b2')
      'p/a/'
      >>> # contrast with cs.lex.common_prefix
      >>> common_prefix('abc/def', 'abc/def1')
      'abc/def'
      >>> common_path_prefix('abc/def', 'abc/def1')
      'abc/'
      >>> common_prefix('abc/def', 'abc/def1', 'abc/def2')
      'abc/def'
      >>> common_path_prefix('abc/def', 'abc/def1', 'abc/def2')
      'abc/'



  Compare the contents of two file-like objects `f1` and `f2` for equality.

  If `f1` or `f2` is a string, open the named file using `mode`
  (default: `"rb"`).



  Copy `nbytes` of data from `fpin` to `fpout`,
  return the number of bytes copied.

  Parameters:
  * `nbytes`: number of bytes to copy.
    If `None`, copy until EOF.
  * `rsize`: read size, default `DEFAULT_READSIZE`.



  Crop a file basename so as not to exceed `name_max` in length.
  Return the original `name` if it already short enough.
  Otherwise crop `name` before the file extension
  to make it short enough.

  Parameters:
  * `name`: the file basename to crop
  * `ext`: optional file extension;
    the default is to infer the extension with `os.path.splitext`.
  * `name_max`: optional maximum length, default: `255`



  General purpose reader for files yielding data from `offset`.

  *WARNING*: this function might move the file pointer.

  Parameters:
  * `f`: the file from which to read data;
    if a string, the file is opened with mode="rb";
    if an int, treated as an OS file descriptor;
    otherwise presumed to be a file-like object.
    If that object has a `.fileno()` method, treat that as an
    OS file descriptor and use it.
  * `offset`: starting offset for the data
  * `maxlength`: optional maximum amount of data to yield
  * `readsize`: read size, default DEFAULT_READSIZE.

  For file-like objects, the read1 method is used in preference
  to read if available. The file pointer is briefly moved during
  fetches.



  General purpose reader for file descriptors yielding data from `offset`.
  **Note**: This does not move the file descriptor position
  **if** the file is seekable.

  Parameters:
  * `fd`: the file descriptor from which to read.
  * `offset`: the offset from which to read.
    If omitted, use the current file descriptor position.
  * `readsize`: the read size, default: `DEFAULT_READSIZE`
  * `aligned`: if true (the default), the first read is sized
    to align the new offset with a multiple of `readsize`.
  * `maxlength`: if specified yield no more than this many bytes of data.



  A decorator which caches a value obtained from a file.

  In addition to all the keyword arguments for `@cs.deco.cachedmethod`,
  this decorator also accepts the following arguments:
  * `attr_name`: the name for the associated attribute, used as
    the basis for the internal cache value attribute
  * `filename`: the filename to monitor.
    Default from the `._{attr_name}__filename` attribute.
    This value will be passed to the method as the `filename` keyword
    parameter.
  * `poll_delay`: delay between file polls, default `DEFAULT_POLL_INTERVAL`.
  * `sig_func`: signature function used to encapsulate the relevant
    information about the file; default
    cs.filestate.FileState({filename}).

  If the decorated function raises OSError with errno == ENOENT,
  this returns None. Other exceptions are reraised.



  Read `nbytes` of data from `fp` and yield the chunks as read.

  Parameters:
  * `nbytes`: number of bytes to read; if None read until EOF.
  * `rsize`: read size, default DEFAULT_READSIZE.



  A property whose value reloads if a file changes.



  A property whose value reloads if any of a list of files changes.

  Note: this is just the default mode for `make_files_property`.

  `func` accepts the file path and returns the new value.
  The underlying attribute name is `'_'+func.__name__`,
  the default from `make_files_property()`.
  The attribute *{attr_name}*`_lock` is a mutex controlling access to the property.
  The attributes *{attr_name}*`_filestates` and *{attr_name}*`_paths` track the
  associated file states.
  The attribute *{attr_name}*`_lastpoll` tracks the last poll time.

  The decorated function is passed the current list of files
  and returns the new list of files and the associated value.

  One example use would be a configuration file with recurive
  include operations; the inner function would parse the first
  file in the list, and the parse would accumulate this filename
  and those of any included files so that they can be monitored,
  triggering a fresh parse if one changes.

  Example:

      class C(object):
        def __init__(self):
          self._foo_path = '.foorc'
        @files_property
        def foo(self,paths):
          new_paths, result = parse(paths[0])
          return new_paths, result

  The load function is called on the first access and on every
  access thereafter where an associated file's `FileState` has
  changed and the time since the last successful load exceeds
  the poll_rate (1s). An attempt at avoiding races is made by
  ignoring reloads that raise exceptions and ignoring reloads
  where files that were stat()ed during the change check have
  changed state after the load.



  Walk a directory tree `path`
  yielding selected paths.

  Note: not selecting a directory prunes all its descendants.



  Test the pathname `abspath(path)` and each of its ancestors
  against the callable `test`,
  yielding paths satisfying the test.

  If `first` is true (default `False`)
  this function always yields exactly one value,
  either the first path satisfying the test or `None`.
  This mode supports a use such as:

      matched_path = next(findup(path, test, first=True))
      
      



  Context manager to open a file which may be a plain file or a gzipped file.

  If `path` ends with `'.gz'` then the filesystem paths attempted
  are `path` and `path` without the extension, otherwise the
  filesystem paths attempted are `path+'.gz'` and `path`.  In
  this way a path ending in `'.gz'` indicates a preference for
  a gzipped file otherwise an uncompressed file.

  However, if exactly one of the paths exists already then only
  that path will be used.

  Note that the single character modes `'r'`, `'a'`, `'w'` and `'x'`
  are text mode for both uncompressed and gzipped opens,
  like the builtin `open` and *unlike* `gzip.open`.
  This is to ensure equivalent behaviour.



  Iterate over data from the file descriptor `fd`.



  Iterate over data from the file `f`.



  Generator yielding lines from a file until EOF.
  Intended for file-like objects that lack a line iteration API.



  A context manager which takes and holds a lock file.
  An open file descriptor is kept for the lock file as well
  to aid locating the process holding the lock file using eg `lsof`.
  This is just a context manager shim for `makelockfile`
  and all arguments are plumbed through.



  Construct a decorator that watches multiple associated files.

  Parameters:
  * `attr_name`: the underlying attribute, default: `'_'+func.__name__`
  * `unset_object`: the sentinel value for "uninitialised", default: `None`
  * `poll_rate`: how often in seconds to poll the file for changes,
    default from `DEFAULT_POLL_INTERVAL`: `1.0`

  The attribute *attr_name*`_lock` controls access to the property.
  The attributes *attr_name*`_filestates` and *attr_name*`_paths` track the
  associated files' state.
  The attribute *attr_name*`_lastpoll` tracks the last poll time.

  The decorated function is passed the current list of files
  and returns the new list of files and the associated value.

  One example use would be a configuration file with recursive
  include operations; the inner function would parse the first
  file in the list, and the parse would accumulate this filename
  and those of any included files so that they can be monitored,
  triggering a fresh parse if one changes.

  Example:

      class C(object):
        def __init__(self):
          self._foo_path = '.foorc'
        @files_property
        def foo(self,paths):
          new_paths, result = parse(paths[0])
          return new_paths, result

  The load function is called on the first access and on every
  access thereafter where an associated file's `FileState` has
  changed and the time since the last successful load exceeds
  the `poll_rate`.

  An attempt at avoiding races is made by
  ignoring reloads that raise exceptions and ignoring reloads
  where files that were `os.stat()`ed during the change check have
  changed state after the load.



  Create a lockfile and return its path.

  The lockfile can be removed with `os.remove`.
  This is the core functionality supporting the `lockfile()`
  context manager.

  Parameters:
  * `path`: the base associated with the lock file,
    often the filesystem object whose access is being managed.
  * `ext`: the extension to the base used to construct the lockfile name.
    Default: ".lock"
  * `timeout`: maximum time to wait before failing.
    Default: `None` (wait forever).
    Note that zero is an accepted value
    and requires the lock to succeed on the first attempt.
  * `poll_interval`: polling frequency when timeout is not 0.
  * `runstate`: optional `RunState` duck instance supporting cancellation.
    Note that if a cancelled `RunState` is provided
    no attempt will be made to make the lockfile.
  * `keepopen`: optional flag, default `False`:
    if true, do not close the lockfile and return `(lockpath,lockfd)`
    being the lock file path and the open file descriptor



  Compute the highest existing numeric suffix
  for names starting with `prefix`.

  This is generally used as a starting point for picking
  a new numeric suffix.



  Create a new directory named `path+sep+n`,
  where `n` exceeds any name already present.

  Parameters:
  * `path`: the basic directory path.
  * `sep`: a separator between `path` and `n`.
    Default: `''`



  A context manager yielding a temporary copy of `filename`
  as returned by `NamedTemporaryFile(**nt_kw)`.

  Parameters:
  * `f`: the name of the file to copy, or an open binary file,
    or a `CornuCopyBuffer`
  * `progress`: an optional progress indicator, default `False`;
    if a `bool`, show a progress bar for the copy phase if true;
    if an `int`, show a progress bar for the copy phase
    if the file size equals or exceeds the value;
    otherwise it should be a `cs.progress.Progress` instance
  * `progress_label`: option progress bar label,
    only used if a progress bar is made
  Other keyword parameters are passed to `tempfile.NamedTemporaryFile`.



  Writable file that discards its input.

  Note that this is _not_ an open of `os.devnull`;
  it just discards writes and is not the underlying file descriptor.

  *`NullFile.__init__(self)`*:
  Initialise the file offset to 0.

  *`NullFile.flush(self)`*:
  Flush buffered data to the subsystem.

  *`NullFile.write(self, data)`*:
  Discard data, advance file offset by length of data.



  Subclass of str presenting convenience properties useful for
  format strings related to file paths.

  *`Pathname.__format__(self, fmt_spec)`*:
  Calling format(<Pathname>, fmt_spec) treat `fmt_spec` as a new style
  formatting string with a single positional parameter of `self`.

  *`Pathname.abs`*:
  The absolute form of this Pathname.

  *`Pathname.basename`*:
  The basename of this Pathname.

  *`Pathname.dirname`*:
  The dirname of the Pathname.

  *`Pathname.isabs`*:
  Whether this Pathname is an absolute Pathname.

  *`Pathname.short`*:
  The shortened form of this Pathname.

  *`Pathname.shorten(self, prefixes=None)`*:
  Shorten a Pathname using ~ and ~user.



  Watch a file for modification by polling its state as obtained
  by `FileState()`.
  Call `reload_file(path)` if the state changes.
  Return `(new_state,reload_file(path))` if the file was modified
  and was unchanged (stable state) before and after the reload_file().
  Otherwise return `(None,None)`.

  This may raise an `OSError` if the `path` cannot be `os.stat()`ed
  and of course for any exceptions that occur calling `reload_file`.

  If `missing_ok` is true then a failure to `os.stat()` which
  raises `OSError` with `ENOENT` will just return `(None,None)`.



  Read `nbytes` of data from `fp`, return the data.

  Parameters:
  * `nbytes`: number of bytes to copy.
    If `None`, copy until EOF.
  * `rsize`: read size, default `DEFAULT_READSIZE`.



  Generator to present text or data from an open file until EOF.

  Parameters:
  * `rsize`: read size, default: DEFAULT_READSIZE
  * `tail_mode`: if true, yield an empty chunk at EOF, allowing resumption
    if the file grows.



  Useful read methods to accomodate modes not necessarily available in a class.

  Note that this mixin presumes that the attribute `self._lock`
  is a threading.RLock like context manager.

  Classes using this mixin should consider overriding the default
  .datafrom method with something more efficient or direct.

  *`ReadMixin.bufferfrom(self, offset)`*:
  Return a CornuCopyBuffer from the specified `offset`.

  *`ReadMixin.datafrom(self, offset, readsize=None)`*:
  Yield data from the specified `offset` onward in some
  approximation of the "natural" chunk size.

  *NOTE*: UNLIKE the global datafrom() function, this method
  MUST NOT move the logical file position. Implementors may need
  to save and restore the file pointer within a lock around
  the I/O if they do not use a direct access method like
  os.pread.

  The aspiration here is to read data with only a single call
  to the underlying storage, and to return the chunks in
  natural sizes instead of some default read size.

  Classes using this mixin must implement this method.

  *`ReadMixin.read(self, size=-1, offset=None, longread=False)`*:
  Read up to `size` bytes, honouring the "single system call"
  spirit unless `longread` is true.

  Parameters:
  * `size`: the number of bytes requested. A size of -1 requests
    all bytes to the end of the file.
  * `offset`: the starting point of the read; if None, use the
    current file position; if not None, seek to this position
    before reading, even if `size` == 0.
  * `longread`: switch from "single system call" to "as many
    as required to obtain `size` bytes"; short data will still
    be returned if the file is too short.

  *`ReadMixin.read_n(self, n)`*:
  Read `n` bytes of data and return them.

  Unlike traditional file.read(), RawIOBase.read() may return short
  data, thus this workalike, which may only return short data if it
  hits EOF.

  *`ReadMixin.readinto(self, barray)`*:
  Read data into a bytearray.



  Rewrite the file `filepath` with data from the file object `srcf`.
  Return `True` if the content was changed, `False` if unchanged.

  Parameters:
  * `filepath`: the name of the file to rewrite.
  * `srcf`: the source file containing the new content.
  * `mode`: the write-mode for the file, default `'w'` (for text);
    use `'wb'` for binary data.
  * `empty_ok`: if true (default `False`),
    do not raise `ValueError` if the new data are empty.
  * `overwrite_anyway`: if true (default `False`),
    skip the content check and overwrite unconditionally.
  * `backup_ext`: if a nonempty string,
    take a backup of the original at `filepath + backup_ext`.
  * `do_diff`: if not `None`, call `do_diff(filepath,tempfile)`.
  * `do_rename`: if true (default `False`),
    rename the temp file to `filepath`
    after copying the permission bits.
    Otherwise (default), copy the tempfile to `filepath`;
    this preserves the file's inode and permissions etc.



  Rewrite a file, presented as a context manager.

  Parameters:
  * `mode`: file write mode, defaulting to "w" for text.

  Other keyword parameters are passed to `rewrite()`.

  Example:

      with rewrite_cmgr(pathname, do_rename=True) as f:
          ... write new content to f ...



  A scratch file for storing data.

  *`RWFileBlockCache.__init__(self, pathname=None, dirpath=None, suffix=None, lock=None)`*:
  Initialise the file.

  Parameters:
  * `pathname`: path of file. If None, create a new file with
    tempfile.mkstemp using dir=`dirpath` and unlink that file once
    opened.
  * `dirpath`: location for the file if made by mkstemp as above.
  * `lock`: an object to use as a mutex, allowing sharing with
    some outer system. A Lock will be allocated if omitted.

  *`RWFileBlockCache.close(self)`*:
  Close the file descriptors.

  *`RWFileBlockCache.closed`*:
  Test whether the file descriptor has been closed.

  *`RWFileBlockCache.get(self, offset, length)`*:
  Get data from `offset` of length `length`.

  *`RWFileBlockCache.put(self, data)`*:
  Store `data`, return offset.



  Rename a path using `os.rename()`,
  but raise an exception if the target path already exists.
  Note: slightly racey.



  Try to test whether a filelike object is seekable.

  First try the `IOBase.seekable` method, otherwise try getting a file
  descriptor from `fp.fileno` and `os.stat()`ing that,
  otherwise return `False`.



  An object with .write, .flush and .close methods
  which copies data to multiple output files.

  *`Tee.__init__(self, *fps)`*:
  Initialise the Tee; any arguments are taken to be output file objects.

  *`Tee.add(self, output)`*:
  Add a new output.

  *`Tee.close(self)`*:
  Close all the outputs and close the Tee.

  *`Tee.flush(self)`*:
  Flush all the outputs.

  *`Tee.write(self, data)`*:
  Write the data to all the outputs.
  Note: does not detect or accodmodate short writes.



  Context manager duplicating `.write` and `.flush` from `fp` to `fp2`.



  Return the pathname of the default temporary directory for scratch data,
  the environment variable `$TMPDIR` or `'/tmp'`.



  Make a new temporary directory with a numeric suffix.



  A `saferename()` that returns `True` on success,
  `False` on failure.





  *Release 20241122*:
  New atomic_copy2 function which is an atomic version of shutil.copy2.

  *Release 20241007.1*:
  Bugfix for @atomic_filename.

  *Release 20241007*:
  atomic_filename: new feature - the caller may remove the temporary file to indicate that the target file should not be made.

  *Release 20240723*:
  lockfile: now a purer shim for makelockfile.

  *Release 20240709*:
  rewrite: return True if the content is modified, False otherwise.

  *Release 20240630*:
  makelockfile: cap the retry poll interval at 37s, just issue a warning if the lock file is already gone on exit (eg manual removal).

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240201*:
  * makelockfile: new optional keepopen parameter - if true return the lock path and an open file descriptor.
  * lockfile(): keep the lock file open to aid debugging with eg lsof.

  *Release 20231129*:
  * atomic_filename: accept optional rename_func to use instead of os.rename, supports using FSTags.move.
  * atomic_filename: clean up the temp file.

  *Release 20230421*:
  atomic_filename: raise FileExistsError instead of ValueError if not exists_ok and existspath(filename).

  *Release 20230401*:
  Replaced a lot of runstate plumbing with @uses_runstate.

  *Release 20221118*:
  atomic_filename: use shutil.copystat instead of shutil.copymode, bugfix the associated logic.

  *Release 20220429*:
  Move longpath and shortpath to cs.fs, leave legacy names behind.

  *Release 20211208*:
  * Move NDJSON stuff to separate cs.ndjson module.
  * New gzifopen() function to open either a gzipped file or an uncompressed file.

  *Release 20210906*:
  Additional release because I'm unsure @atomic_filename made it into the previous release.

  *Release 20210731*:
  New atomic_filename context manager wrapping NamedTemporaryFile for presenting a file after its contents are prepared.

  *Release 20210717*:
  Updates for recent cs.mappings-20210717 release.

  *Release 20210420*:
  * Forensic prefix for NamedTemporaryCopy.
  * UUIDNDJSONMapping: provide an empty .scan_errors on instantiation, avoids AttributeError if a scan never occurs.

  *Release 20210306*:
  * datafrom_fd: fix use-before-set of is_seekable.
  * RWFileBlockCache.put: remove assert(len(data)>0), adjust logic.

  *Release 20210131*:
  crop_name: put ext before name_max, more likely to be specified, I think.

  *Release 20201227.1*:
  Docstring tweak.

  *Release 20201227*:
  scan_ndjson: optional errors_list to accrue errors during the scan.

  *Release 20201108*:
  Bugfix rewrite_cmgr, failed to flush a file before copying its contents.

  *Release 20201102*:
  * Newline delimited JSON (ndjson) support.
  * New UUIDNDJSONMapping implementing a singleton cs.mappings.LoadableMappingMixin of cs.mappings.UUIDedDict subclass instances backed by an NDJSON file.
  * New scan_ndjson() function to yield newline delimited JSON records.
  * New write_ndjson() function to write newline delimited JSON records.
  * New append_ndjson() function to append a single newline delimited JSON record to a file.
  * New NamedTemporaryCopy for creating a temporary copy of a file with an optional progress bar.
  * rewrite_cmgr: turn into a simple wrapper for rewrite.
  * datafrom: make the offset parameter optional, tweak the @strable open function.
  * datafrom_fd: support nonseekable file descriptors, document that for these the file position is moved (no pread support).
  * New iter_fd and iter_file to return iterators of a file's data by utilising a CornuCopyBuffer.
  * New byteses_as_fd to return a readable file descriptor receiving an iterable of bytes via a CornuCopyBuffer.

  *Release 20200914*:
  New common_path_prefix to compare pathnames.

  *Release 20200517*:
  * New crop_name() function to crop a file basename to fit within a specific length.
  * New find() function complimenting findup (UNTESTED).

  *Release 20200318*:
  New findup(path,test) generator to walk up a file tree.

  *Release 20191006*:
  Adjust import of cs.deco.cachedmethod.

  *Release 20190729*:
  datafrom_fd: make `offset` optional, defaulting to fd position at call.

  *Release 20190617*:
  @file_based: adjust use of @cached from cached(wrap0, **dkw) to cached(**dkw)(wrap0).

  *Release 20190101*:
  datafrom: add maxlength keyword arg, bugfix fd and f.fileno cases.

  *Release 20181109*:
  * Various bugfixes for BackedFile.
  * Use a file's .read1 method if available in some scenarios.
  * makelockfile: accept am optional RunState control parameter, improve some behaviour.
  * datafrom_fd: new optional maxlength parameter limiting the amount of data returned.
  * datafrom_fd: by default, perform an initial read to align all subsequent reads with the readsize.
  * drop fdreader, add datafrom(f, offset, readsize) accepting a file or a file descriptor, expose datafrom_fd.
  * ReadMixin.datafrom now mandatory. Add ReadMixin.bufferfrom.
  * Assorted other improvements, minor bugfixes, documentation improvements.

  *Release 20171231.1*:
  Trite DISTINFO fix, no semantic changes.

  *Release 20171231*:
  Update imports, bump DEFAULT_READSIZE from 8KiB to 128KiB.

  *Release 20170608*:
  * Move lockfile and the SharedAppend* classes to cs.sharedfile.
  * BackedFile internal changes.

  *Release 20160918*:
  * BackedFile: redo implementation of .front_file to fix resource leak; add .__len__; add methods .spans, .front_spans and .back_spans to return information about front vs back data.
  * seek: bugfix: seek should return the new file offset.
  * BackedFile does not subclass RawIOBase, it just works like one.

  *Release 20160828*:
  * Use "install_requires" instead of "requires" in DISTINFO.
  * Rename maxFilenameSuffix to max_suffix.
  * Pull in OpenSocket file-like socket wrapper from cs.venti.tcp.
  * Update for cs.asynchron changes.
  * ... then move cs.fileutils.OpenSocket into new module cs.socketutils.
  * New Tee class, for copying output to multiple files.
  * NullFile class which discards writes (==> no-op for Tee).
  * New class SavingFile to accrue output and move to specified pathname when complete.
  * Memory usage improvements.
  * Polyfill non-threadsafe implementation of pread if os.pread does not exist.
  * New function seekable() to probe a file for seekability.
  * SharedAppendFile: provide new .open(filemode) context manager for allowing direct file output for external users.
  * New function makelockfile() presenting the logic to create a lock file separately from the lockfile context manager.
  * Assorted bugfixes and improvements.

  *Release 20150116*:
  Initial PyPI release.


pkgId: 3a7b41c93dff2d4a129fca2e95929ff66e4c15d0c04f234eb3ec14c433a2de73
name: python-cs-fs
arch: src
version: 20241122
epoch: 0
release: 1
url: https://pypi.org/project/cs-fs/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-fs-20241122-1.src.rpm
checksum_type: sha256
summary:
  Assorted filesystem related utility functions, some of which have been bloating cs.fileutils for too long.

description:
  Assorted filesystem related utility functions,
  some of which have been bloating cs.fileutils for too long.

  *Latest release 20241122*:
  * FSPathBasedSingleton: add a .promote method to promote a filesystem path to an instance.
  * FSPathBasedSingleton: new fspath_normalised(fspath) class method to produce a normalised form of the fspath for use as the key to the singleton registry.



  Decorator for a function which fills in a directory
  which calls the function against a temporary directory
  then renames the temporary to the target name on completion.

  Parameters:
  * `infill_func`: the function to fill in the target directory
  * `make_placeholder`: optional flag, default `False`:
    if true an empty directory will be make at the target name
    and after completion it will be removed and the completed
    directory renamed to the target name



  Walk up the filesystem tree looking for a directory where
  `criterion(fspath)` is not `None`, where `fspath` starts at `dirpath`.
  Return the result of `criterion(fspath)`.
  Return `None` if no such path is found.

  Parameters:
  * `dirpath`: the starting directory
  * `criterion`: a `str` or a callable accepting a `str`

  If `criterion` is a `str`, use look for the existence of `os.path.join(fspath,criterion)`

  Example:

      
      envrc_path = findup('.', '.envrc')

      
      rules_path = findup(expanduser('~/Downloads', '.taggerrc')



  Return a list of the names in `dirpath` matching the glob `fnglob`.



  The basis for a `SingletonMixin` based on `realpath(self.fspath)`.

  *`FSPathBasedSingleton.__init__(self, fspath: Optional[str] = None, lock=None)`*:
  Initialise the singleton:

  On the first call:
  - set `.fspath` to `self._resolve_fspath(fspath)`
  - set `._lock` to `lock` (or `cs.threads.NRLock()` if not specified)

  *`FSPathBasedSingleton.fspath_normalised(fspath: str)`*:
  Return the normalised form of the filesystem path `fspath`,
  used as the key for the singleton registry.

  This default returns `realpath(fspath)`.

  As a contracting example, the `cs.ebooks.kindle.classic.KindleTree`
  class tries to locate the directory containing the book
  database, and returns its realpath, allowing some imprecision.

  *`FSPathBasedSingleton.promote(obj)`*:
  Promote `None` or `str` to a `CalibreTree`.



  A mixin for an object with a `.fspath` attribute representing a filesystem location.

  The `__init__` method just sets the `.fspath` attribute, and
  need not be called if the main class takes care of that itself.

  *`HasFSPath.fnmatch(self, fnglob)`*:
  Return a list of the names in `self.fspath` matching the glob `fnglob`.

  *`HasFSPath.listdir(self)`*:
  Return `os.listdir(self.fspath)`.

  *`HasFSPath.pathto(self, *subpaths)`*:
  The full path to `subpaths`, comprising a relative path
  below `self.fspath`.
  This is a shim for `os.path.join` which requires that all
  the `subpaths` be relative paths.

  *`HasFSPath.shortpath`*:
  The short version of `self.fspath`.



  Test that `rpath` is a clean relative path with no funny business.

  This is a Boolean wrapper for `validate_rpath()`.



  Return `path` with prefixes and environment variables substituted.
  The converse of `shortpath()`.



  Create the directory `dirpath` if missing.
  Return `True` if the directory was made, `False` otherwise.

  Parameters:
  * `dirpath`: the required directory path
  * `mode`: the permissions mode, default `0o777`
  * `log`: log `makedirs` or `mkdir` call
  * `use_makedirs`: optional creation mode, default `False`;
    if true, use `os.makedirs`, otherwise `os.mkdir`



  A shim for `scandirtree` to yield relative file paths from a directory.

  Parameters:
  * `dirpath`: optional top directory, default `'.'`

  Other keyword arguments are passed to `scandirtree`.



  A shim for `scandirtree` to yield filesystem paths from a directory.

  Parameters:
  * `dirpath`: optional top directory, default `'.'`

  Other keyword arguments are passed to `scandirtree`.



  Generator to recurse over `dirpath`, yielding `(is_dir,subpath)`
  for all selected subpaths.

  Parameters:
  * `dirpath`: the directory to scan, default `'.'`
  * `include_dirs`: if true yield directories; default `False`
  * `name_selector`: optional callable to select particular names;
    the default is to select names not starting with a dot (`'.'`)
  * `only_suffixes`: if supplied, skip entries whose extension
    is not in `only_suffixes`
  * `skip_suffixes`: if supplied, skip entries whose extension
    is in `skip_suffixes`
  * `sort_names`: option flag, default `False`; yield entires
    in lexical order if true
  * `follow_symlinks`: optional flag, default `False`; passed to `scandir`
  * `recurse`: optional flag, default `True`; if true, recurse into subdrectories



  Return `fspath` with the first matching leading prefix replaced.

  Parameters:
  * `prefixes`: optional list of `(prefix,subst)` pairs
  * `collapseuser`: optional flag to enable detection of user
    home directory paths; default `False`
  * `foldsymlinks`: optional flag to enable detection of
    convenience symlinks which point deeper into the path;
    default `False`

  The `prefixes` is an optional iterable of `(prefix,subst)`
  to consider for replacement.  Each `prefix` is subject to
  environment variable substitution before consideration.
  The default `prefixes` is from `SHORTPATH_PREFIXES_DEFAULT`:
  `(('$HOME/', '~/'),)`.



  Test that `rpath` is a clean relative path with no funny business;
  raise `ValueError` if the test fails.

  Tests:
  - not empty or '.' or '..'
  - not an absolute path
  - normalised
  - does not walk up out of its parent directory

  Examples:

      >>> validate_rpath('')
      False
      >>> validate_rpath('.')





  *Release 20241122*:
  * FSPathBasedSingleton: add a .promote method to promote a filesystem path to an instance.
  * FSPathBasedSingleton: new fspath_normalised(fspath) class method to produce a normalised form of the fspath for use as the key to the singleton registry.

  *Release 20241007*:
  FSPathBasedSingleton.__init__: use an NRLock for the default lock, using a late import with fallback to Lock.

  *Release 20241005*:
  needdir: now returns True if the directory was made, False if it already existed.

  *Release 20240630*:
  FSPathBasedSingleton: recent Pythons seem to check that __init__ returns None, subclasses must test another way.

  *Release 20240623*:
  * shortpath(foldsymlinks=True): only examine symlinks which have clean subpaths in their link text - this avoids junk and also avoids stat()ing links which might be symlinks to mount points which might be offline.
  * scandirtree: clean up the logic, possibly fix repeated mention of directories.

  *Release 20240522*:
  shortpath: new collapseuser=False, foldsymlinks=False parameters, rename DEFAULT_SHORTEN_PREFIXES to SHORTPATH_PREFIXES_DEFAULT.

  *Release 20240422*:
  New scandirtree scandir based version of os.walk, yielding (is_dir,fspath). New shim scandirpaths.

  *Release 20240412*:
  HasFSPath: explain that the __init__ is optional in the docstring.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240201*:
  * FSPathBasedSingleton: drop the default_factory parameter/attribute, let default_attr specify a callable.
  * Singleton._resolve_fspath: fix reference to class name.

  *Release 20231129*:
  * HasFSPath: new listdir method.
  * HasFSPath.pathto: accept multiple relative subpaths.
  * FSPathBasedSingleton: accept cls.FSPATH_FACTORY as a factory function for the default fspath, makes it possible to defer the path lookup.
  * Replace is_clean_subpath with validate_rpath/is_valid_rpath pair.

  *Release 20230806*:
  * Reimplement fnmatchdir using fnmatch.filter.
  * No longer claim Python 2 compatibility.

  *Release 20230401*:
  HasFSPath.shortpath: hand call before .fspath set.

  *Release 20221221*:
  Replace use of cs.env.envsub with os.path.expandvars and drop unused environ parameter.

  *Release 20220918*:
  * FSPathBasedSingleton.__init__: return True on the first call, False on subsequent calls.
  * FSPathBasedSingleton.__init__: probe __dict__ for '_lock' instead of using hasattr (which plays poorly this early on with classes with their own __getattr__).
  * needdir: accept optional `log` parameter to log mkdir or makedirs.
  * HasFSPath: add a default __str__.

  *Release 20220805*:
  Doc update.

  *Release 20220530*:
  FSPathBasedSingleton._resolve_fspath: new `envvar` and `default_attr` parameters.

  *Release 20220429*:
  * New HasFSPath and FSPathBasedSingleton.
  * Add longpath and shortpath from cs.fileutils.
  * New is_clean_subpath(subpath).
  * New needdir(path).
  * New fnmatchdir(dirpath,fnglob) pulled out from HasFSPath.fnmatch(fnglob).

  *Release 20220327*:
  New module cs.fs to contain more filesystem focussed functions than cs.fileutils, which is feeling a bit bloated.


pkgId: 581f5b4b1a1079b1cf6ea6a218ee8ab67714c4c580247e88834d8806e53b4061
name: python-cs-hashindex
arch: src
version: 20241207
epoch: 0
release: 1
url: https://pypi.org/project/cs-hashindex/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-hashindex-20241207-1.src.rpm
checksum_type: sha256
summary:
  A command and utility functions for making listings of file content hashcodes and manipulating directory trees based on such a hash index.

description:
  A command and utility functions for making listings of file content hashcodes
  and manipulating directory trees based on such a hash index.

  *Latest release 20241207*:
  Mostly CLI usage improvements.

  This largely exists to solve my "what has changed remotely?" or
  "what has been filed where?" problems by comparing file trees
  using the files' content hashcodes.

  This does require reading every file once to compute its hashcode,
  but the hashcodes (and file sizes and mtimes when read) are
  stored beside the file in `.fstags` files (see the `cs.fstags`
  module), so that a file does not need to be reread on subsequent
  comparisons.

  `hashindex` knows how to invoke itself remotely using `ssh`
  (this does require `hashindex` to be installed on the remote host)
  and can thus be used to compare a local and remote tree, for example:

      hashindex comm -1 localtree remotehost:remotetree

  When you point `hashindex` at a remote tree, it uses `ssh` to
  run `hashindex` on the remote host, so all the content hashing
  is done locally to the remote host instead of copying files
  over the network.

  You can also use it to rearrange a tree based on the locations
  of corresponding files in another tree. Consider a media tree
  replicated between 2 hosts. If the source tree gets rearranged,
  the destination can be equivalently rearranged without copying
  the files, for example:

      hashindex rearrange sourcehost:sourcetree localtree

  If `fstags mv` was used to do the original rearrangement then
  the hashcodes will be copied to the new locations, saving a
  rescan of the source file. I keep a shell alias `mv="fstags mv"`
  so this is routine for me.

  I have a backup script [`histbackup`](https://hg.sr.ht/~cameron-simpson/css/browse/bin/histbackup)
  which works by making a hard link tree of the previous backup
  and `rsync`ing into it.  It has long been subject to huge
  transfers if the source tree gets rearranged. Now it has a
  `--hashindex` option to get it to run a `hashindex rearrange`
  between the hard linking to the new backup tree and the `rsync`
  from the source to the new tree.

  If network bandwith is limited or quotaed, you can use the
  comparison function to prepare a list of files missing from the
  remote location and copy them to a transfer drive for carrying
  to the remote site when opportune. Example:

      hashindex comm -1 -o '{fspath}' src rhost:dst \
      | rsync -a --files-from=- src/ xferdir/

  I've got a script [`pref-xfer`](https://hg.sr.ht/~cameron-simpson/css/browse/bin-cs/prep-xfer)
  which does this with some conveniences and sanity checks.



  Generator yielding the filesystem paths of the files in `dirpath`.



  Generator yielding `(srcpath,[remapped_paths])` 2-tuples
  based on the hashcodes keying `fspaths_by_hashcode`.



  Return the hashcode for the contents of the file at `fspath`.
  Warn and return `None` on `OSError`.



  Generator yielding `(hashcode,filepath)` 2-tuples
  for the files in `fspath`, which may be a file or directory path.
  Note that it yields `(None,filepath)` for files which cannot be accessed.



  A tool to generate indices of file content hashcodes
  and to link or otherwise rearrange files to destinations based
  on their hashcode.

  Usage summary:

      Usage: hashindex [common-options...] subcommand...
        Generate or process file content hash listings.
        Subcommands:
          comm [common-options...] {-1|-2|-3|-r} {path1|-} {path2|-}
            Compare the filepaths in path1 and path2 by content.
            Options:
              -1  List hashes and paths only present in path1.
              -2  List hashes and paths only present in path2.
              -3  List hashes and paths present in path1 and path2.
              -r  Emit relative paths in the listing.
          help [common-options...] [-l] [-s] [subcommand-names...]
            Print help for subcommands.
            This outputs the full help for the named subcommands,
            or the short help for all subcommands if no names are specified.
            Options:
              -l  Long listing.
              -r  Recurse into subcommands.
              -s  Short listing.
          info [common-options...] [field-names...]
            Recite general information.
            Explicit field names may be provided to override the default listing.
          ls [common-options...] [options...] [[host:]path...]
            Walk filesystem paths and emit a listing.
            The default path is the current directory.
            Options:
              -r  Emit relative paths in the listing.
                  This requires each path to be a directory.
          rearrange [common-options...] [options...] {[[user@]host:]refdir|-} [[user@]rhost:]targetdir [dstdir]
            Rearrange files from targetdir into dstdir based on their positions in refdir.
            Other arguments:
              refdir      The reference directory, which may be local or remote
                          or "-" indicating that a hash index will be read from
                          standard input.
              targetdir   The directory containing the files to be rearranged,
                          which may be local or remote.
              dstdir      Optional destination directory for the rearranged files.
                          Default is the targetdir.
                          It is taken to be on the same host as targetdir.
            Options:
              --mv  Move mode.
              -s    Synmlink mode.
          shell [common-options...]
            Run a command prompt via cmd.Cmd using this command's subcommands.

  *`HashIndexCommand.Options`*

  *`HashIndexCommand.cmd_comm(self, argv, *, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x10e974cc0>)`*:
  Usage: {cmd} {{-1|-2|-3|-r}} {{path1|-}} {{path2|-}}
  Compare the filepaths in path1 and path2 by content.
  Options:
    -1  List hashes and paths only present in path1.
    -2  List hashes and paths only present in path2.
    -3  List hashes and paths present in path1 and path2.
    -r  Emit relative paths in the listing.

  *`HashIndexCommand.cmd_ls(self, argv, *, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x10e975120>)`*:
  Usage: {cmd} [options...] [[host:]path...]
  Walk filesystem paths and emit a listing.
  The default path is the current directory.
  Options:
    -r  Emit relative paths in the listing.
        This requires each path to be a directory.

  *`HashIndexCommand.cmd_rearrange(self, argv)`*:
  Usage: {cmd} [options...] {{[[user@]host:]refdir|-}} [[user@]rhost:]targetdir [dstdir]
  Rearrange files from targetdir into dstdir based on their positions in refdir.
  Other arguments:
    refdir      The reference directory, which may be local or remote
                or "-" indicating that a hash index will be read from
                standard input.
    targetdir   The directory containing the files to be rearranged,
                which may be local or remote.
    dstdir      Optional destination directory for the rearranged files.
                Default is the targetdir.
                It is taken to be on the same host as targetdir.
  Options:
    --mv  Move mode.
    -s    Synmlink mode.



  Return a filesystem path modified so that it connot be
  misinterpreted as a remote path such as `user@host:path`.

  If `fspath` contains no colon (`:`) or is an absolute path
  or starts with `./` then it is returned unchanged.
  Otherwise a leading `./` is prepended.



  Commandline implementation.



  Merge `srcpath` to `dstpath`.

  If `dstpath` does not exist, move/link/symlink `srcpath` to `dstpath`.
  Otherwise checksum their contents and raise `FileExistsError` if they differ.



  Generator yielding `(srcpath,fspaths)` 2-tuples.



  A generator which reads line from the file `f`
  and yields `(hashcode,fspath)` 2-tuples for each line.
  If there are parse errors the `hashcode` or `fspath` may be `None`.



  A generator which reads a hashindex of a remote directory,
  This runs: `hashindex ls -h hashname -r rdirpath` on the remote host.
  It yields `(hashcode,fspath)` 2-tuples.

  Parameters:
  * `rhost`: the remote host, or `user@host`
  * `rdirpath`: the remote directory path
  * `hashname`: the file content hash algorithm name
  * `ssh_exe`: optional `ssh` command
  * `hashindex_exe`: the remote `hashindex` executable
  * `relative`: optional flag, default `False`;
    if true pass `'-r'` to the remote `hashindex ls` command
  * `check`: whether to check that the remote command has a `0` return code,
    default `True`



  Rearrange the files in `dirpath` according to the
  hashcode->[relpaths] `fspaths_by_hashcode`.

  Parameters:
  * `srcdirpath`: the directory whose files are to be rearranged
  * `rfspaths_by_hashcode`: a mapping of hashcode to relative
    pathname to which the original file is to be moved
  * `dstdirpath`: optional target directory for the rearranged files;
    defaults to `srcdirpath`, rearranging the files in place
  * `hashname`: the file content hash algorithm name
  * `move_move`: move files instead of linking them
  * `symlink_mode`: symlink files instead of linking them
  * `doit`: if true do the link/move/symlink, otherwise just print



  Run a remote `hashindex` command.
  Return the `CompletedProcess` result or `None` if `doit` is false.
  Note that as with `cs.psutils.run`, the arguments are resolved
  via `cs.psutils.prep_argv`.

  Parameters:
  * `rhost`: the remote host, or `user@host`
  * `argv`: the command line arguments to be passed to the
    remote `hashindex` command
  * `check`: whether to check that the remote command has a `0` return code,
    default `True`
  Other keyword parameters are passed therough to `cs.psutils.run`.





  *Release 20241207*:
  Mostly CLI usage improvements.

  *Release 20241007*:
  Small internal changes.

  *Release 20240709*:
  * Require `blake3` and use it as the default hash algorithm.
  * Some internal improvements.

  *Release 20240623*:
  hashindex: plumb hashname to file_checksum.

  *Release 20240412*:
  * file_checksum: skip any nonregular file, only use run_task when checksumming more than 1MiB.
  * HashIndexCommand.cmd_rearrange: run the refdir index in relative mode.
  * Small fixes.

  *Release 20240317*:
  * HashIndexCommand.cmd_ls: default to listing the current directory.
  * HashIndexCommand: new -o output_format to allow outputting only hashcodes or fspaths.
  * HashIndexCommand.cmd_comm: new -r (relative) option.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240305*:
  * HashIndexCommand.cmd_ls: support rhost:rpath paths, honour intterupts in the remote mode.
  * HashIndexCommand.cmd_rearrange: new optional dstdir command line argument, passed to rearrange.
  * merge: symlink_mode: leave identical symlinks alone, just merge tags.
  * rearrange: new optional dstdirpath parameter, default srcdirpath.

  *Release 20240216*:
  * HashIndexCommand.cmdlinkto,cmd_rearrange: run the link/mv stuff with sys.stdout in line buffered mode.
  * DO not get hashcodes from symlinks.
  * HashIndexCommand.cmd_ls: ignore None hashcodes, do not set xit=1.
  * New run_remote_hashindex() and read_remote_hashindex() functions.
  * dir_filepaths: skip dot files, the fstags .fstags file and nonregular files.

  *Release 20240211.1*:
  Better module docstring.

  *Release 20240211*:
  Initial PyPI release: "hashindex" command and utility functions for listing file hashcodes and rearranging trees based on a hash index.


pkgId: e84d4d3dc02b4b512ba9037cd76118f9003f3077dd7059dd84b3eb358d68cca5
name: python-cs-hashutils
arch: src
version: 20241207
epoch: 0
release: 1
summary: Convenience hashing facilities.
url: https://pypi.org/project/cs-hashutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-hashutils-20241207-1.src.rpm
checksum_type: sha256
description:
  Convenience hashing facilities.

  *Latest release 20241207*:
  BaseHashCode.hashclass: raise ValueError from unknown hash function name with greater detail on the underlying failure.



  Base class for hashcodes, subclassed by `SHA1`, `SHA256` et al.

  You can obtain the class for a particular hasher by name, example:

      SHA256 = BaseHashCode.hashclass('sha256')

  *`BaseHashCode.__str__(self)`*:
  Return `f'{self.hashname}:{self.hex()}'`.

  *`BaseHashCode.from_buffer(bfr: cs.buffer.CornuCopyBuffer)`*:
  Compute hashcode from the contents of the `CornuCopyBuffer` `bfr`.

  *`BaseHashCode.from_data(bs)`*:
  Compute hashcode from the data `bs`.

  *`BaseHashCode.from_fspath(fspath, **kw)`*:
  Compute hashcode from the contents of the file `fspath`.

  *`BaseHashCode.from_hashbytes(hashbytes)`*:
  Factory function returning a `BaseHashCode` object from the hash bytes.

  *`BaseHashCode.from_hashbytes_hex(hashhex: str)`*:
  Factory function returning a `BaseHashCode` object
  from the hash bytes hex text.

  *`BaseHashCode.from_named_hashbytes_hex(hashname, hashhex)`*:
  Factory function to return a `HashCode` object
  from the hash type name and the hash bytes hex text.

  *`BaseHashCode.from_prefixed_hashbytes_hex(hashtext: str)`*:
  Factory function returning a `BaseHashCode` object
  from the hash bytes hex text prefixed by the hashname.
  This is the reverse of `__str__`.

  *`BaseHashCode.get_hashfunc(hashname: str)`*:
  Fetch the hash function implied by `hashname`.

  *`BaseHashCode.hashclass(hashname: str, hashfunc=None, **kw)`*:
  Return the class for the hash function named `hashname`.

  Parameters:
  * `hashname`: the name of the hash function
  * `hashfunc`: optional hash function for the class

  *`BaseHashCode.hashname`*:
  The hash code type name, derived from the class name.

  *`BaseHashCode.hex(self) -> str`*:
  Return the hashcode bytes transcribes as a hexadecimal ASCII `str`.

  *`BaseHashCode.promote(obj)`*:
  Promote to a `BaseHashCode` instance.



  Hash class for the 'md5' algorithm.

  *`MD5.hashfunc`*



  Hash class for the 'sha1' algorithm.

  *`SHA1.hashfunc`*



  Hash class for the 'sha224' algorithm.

  *`SHA224.hashfunc`*



  Hash class for the 'sha256' algorithm.

  *`SHA256.hashfunc`*



  Hash class for the 'sha384' algorithm.

  *`SHA384.hashfunc`*



  Hash class for the 'sha512' algorithm.

  *`SHA512.hashfunc`*





  *Release 20241207*:
  BaseHashCode.hashclass: raise ValueError from unknown hash function name with greater detail on the underlying failure.

  *Release 20240412*:
  * BaseHashCode.hashclass(hashname): fall back to looking for blake3 from the blake3 module.
  * BaseHashCode: new get_hashfunc(hashname) static method.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240211*:
  Initial PyPI release: BaseHashCode(bytes) and subclasses for various hash algorithms.


pkgId: f1d63361a4ef61521b30de2df1dcf85521144b935555cb4f0de4b3dc08ba6e27
name: python-cs-iso14496
arch: src
version: 20241122
epoch: 0
release: 1
url: https://pypi.org/project/cs-iso14496/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-iso14496-20241122-1.src.rpm
checksum_type: sha256
summary:
  Facilities for ISO14496 files - the ISO Base Media File Format, the basis for several things including MP4 and MOV.

description:
  Facilities for ISO14496 files - the ISO Base Media File Format,
  the basis for several things including MP4 and MOV.

  *Latest release 20241122*:
  Replace many raises of RuntimeError with NotImplementedError, suggestion by @dimaqq on disucss.python.org.

  ISO make the standard available here:
  * [available standards main page](http://standards.iso.org/ittf/PubliclyAvailableStandards/index.html)
  * [zip file download](http://standards.iso.org/ittf/PubliclyAvailableStandards/c068960_ISO_IEC_14496-12_2015.zip)



  Create and register a new `BoxBody` class that is simply a subclass of
  another.
  Return the new class.



  Create and add a specific Time to Sample box - section 8.6.1.



  Add a Time to Sample box - section 8.6.1.



  Base class for all boxes - ISO14496 section 4.2.

  This has the following fields:
  * `header`: a `BoxHeader`
  * `body`: a `BoxBody` instance, usually a specific subclass
  * `unparsed`: any unconsumed bytes from the `Box` are stored as here

  *`Box.BOX_TYPE`*:
  The default .BOX_TYPE is inferred from the class name.

  *`Box.__getattr__(self, attr)`*:
  If there is no direct attribute from `SimpleBinary.__getattr__`,
  have a look in the `.header` and `.body`.

  *`Box.__iter__(self)`*:
  Iterating over a `Box` iterates over its body.
  Typically that would be the `.body.boxes`
  but might be the samples if the body is a sample box,
  etc.

  *`Box.ancestor(self, box_type)`*:
  Return the closest ancestor box of type `box_type`.

  *`Box.box_type`*:
  The `Box` header type.

  *`Box.box_type_path`*:
  The type path to this Box.

  *`Box.box_type_s`*:
  The `Box` header type as a string.

  If the header type bytes decode as ASCII, return that,
  otherwise the header bytes' repr().

  *`Box.dump(self, **kw)`*:
  Dump this Box.

  *`Box.gather_metadata(self)`*:
  Walk the `Box` hierarchy looking for metadata.
  Yield `(Box,TagSet)` for each `b'moov'` or `b'trak'` `Box`.

  *`Box.metatags(self)`*:
  Return a `TagSet` containing metadata for this box.

  *`Box.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Decode a `Box` from `bfr` and return it.

  *`Box.parse_field(self, field_name, bfr: cs.buffer.CornuCopyBuffer, binary_cls)`*:
  `parse_field` delegates to the `Box` body `parse_field`.

  *`Box.parse_length`*:
  The length of the box as consumed from the buffer,
  computed as `self.end_offset-self.offset`.

  *`Box.reparse_buffer(self)`*:
  Context manager for continuing a parse from the `unparsed` field.

  Pops the final `unparsed` field from the `Box`,
  yields a `CornuCopyBuffer` make from it,
  then pushes the `unparsed` field again
  with the remaining contents of the buffer.

  *`Box.self_check(self)`*:
  Sanity check this Box.

  *`Box.transcribe(self)`*:
  Transcribe the `Box`.

  Before transcribing the data, we compute the total box_size
  from the lengths of the current header, body and unparsed
  components, then set the header length if that has changed.
  Since setting the header length can change its representation
  we compute the length again and abort if it isn't stable.
  Otherwise we proceeed with a regular transcription.

  *`Box.unparsed_bs`*:
  The unparsed data as a single `bytes` instance.

  *`Box.user_type`*:
  The header user_type.

  *`Box.walk(self) -> Iterable[Tuple[ForwardRef('Box'), List[ForwardRef('Box')]]]`*:
  Walk this `Box` hierarchy.

  Yields the starting box and its children as `(self,subboxes)`
  and then yields `(subbox,subsubboxes)` for each child in turn.

  As with `os.walk`, the returned `subboxes` list
  may be modified in place to prune or reorder the subsequent walk.



  Abstract basis for all `Box` bodies.

  *`BoxBody.__getattr__(self, attr)`*:
  The following virtual attributes are defined:
  * *TYPE*`s`:
    "boxes of type *TYPE*",
    an uppercased box type name with a training `s`;
    a list of all elements whose `.box_type`
    equals *TYPE*`.lower().encode('ascii')`.
    The elements are obtained by iterating over `self`
    which normally means iterating over the `.boxes` attribute.
  * *TYPE*:
    "the box of type *TYPE*",
    an uppercased box type name;
    the sole element whose box type matches the type,
    obtained from `.`*TYPE*`s[0]`
    with a requirement that there is exactly one match.
  * *TYPE*`0`:
    "the optional box of type *TYPE*",
    an uppercased box type name with a trailing `0`;
    the sole element whose box type matches the type,
    obtained from `.`*TYPE*`s[0]`
    with a requirement that there is exactly zero or one match.
    If there are zero matches, return `None`.
    Otherwise return the matching box.

  *`BoxBody.add_field(self, field_name, value)`*:
  Add a field named `field_name` with the specified `value`
  to the box fields.

  *`BoxBody.boxbody_type_from_class()`*:
  Compute the Box's 4 byte type field from the class name.

  *`BoxBody.for_box_type(box_type: bytes)`*:
  Return the `BoxBody` subclass suitable for the `box_type`.

  *`BoxBody.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Create a new instance and gather the `Box` body fields from `bfr`.

  Subclasses implement a `parse_fields` method to parse additional fields.

  *`BoxBody.parse_boxes(self, bfr: cs.buffer.CornuCopyBuffer, **kw)`*:
  Utility method to parse the remainder of the buffer as a
  sequence of `Box`es.

  *`BoxBody.parse_field(self, field_name, bfr: cs.buffer.CornuCopyBuffer, binary_cls)`*:
  Parse an instance of `binary_cls` from `bfr`
  and store it as the attribute named `field_name`.

  `binary_cls` may also be an `int`, in which case that many
  bytes are read from `bfr`.

  *`BoxBody.parse_field_value(self, field_name, bfr: cs.buffer.CornuCopyBuffer, binary_cls)`*:
  Parse a single value binary, store the value as `field_name`,
  store the instance as the field `field_name+'__Binary'`
  for transcription.

  Note that this disassociaes the plain value attribute
  from what gets transcribed.

  *`BoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse additional fields.
  This base class implementation consumes nothing.

  *`BoxBody.transcribe(self)`*:
  Transcribe the binary structure.

  This default implementation transcribes the fields parsed with the
  `parse_field` method in the order parsed.

  *`BoxBody.transcribe_fields(self)`*:
  Transcribe the fields parsed with the `parse_field` method in the
  order parsed.



  An ISO14496 `Box` header packet.

  *`BoxHeader.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Decode a box header from `bfr`.



  BitRateBoxBody - section 8.5.2.2.

  *`BTRTBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `bufferSizeDB`, `maxBitrate` and `avgBitrate` fields.



  A 'c064' Chunk Offset box - section 8.7.5.

  *`CO64BoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `entry_count` and `chunk_offsets` fields.

  *`CO64BoxBody.transcribe(self)`*:
  Transcribe a `CO64BoxBody`.



  Common subclass of several things with `.boxes`.



  A 'cprt' Copyright box - section 8.10.2.

  *`CPRTBoxBody.language`*:
  The `language_field` as the 3 character ISO 639-2/T language code.

  *`CPRTBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `language` and `notice` fields.



  A 'cslg' Composition to Decode box - section 8.6.1.4.

  *`CSLGBoxBody.CSLGParamsLong`*

  *`CSLGBoxBody.CSLGParamsQuad`*

  *`CSLGBoxBody.__getattr__(self, attr)`*:
  Present the `params` attributes at the top level.

  *`CSLGBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the compositionToDTSShift`, `leastDecodeToDisplayDelta`,
  `greatestDecodeToDisplayDelta`, `compositionStartTime` and
  `compositionEndTime` fields.



  The iTunes 'Date' meta field: a year or an ISO timestamp.



  Dereference a path with respect to this Box.



  A 'dref' Data Reference box containing Data Entry boxes - section 8.7.2.1.

  *`DREFBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `entry_count` and `boxes` fields.



  Recursively dump a Box.



  A ELNGBoxBody is a Extended Language Tag box - ISO14496 section 8.4.6.

  *`ELNGBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `extended_language` field.



  An 'elst' Edit List FullBoxBody - section 8.6.6.

  *`ELSTBoxBody.V0EditEntry`*

  *`ELSTBoxBody.V1EditEntry`*

  *`ELSTBoxBody.entry_class`*:
  The class representing each entry.

  *`ELSTBoxBody.entry_count`*:
  The number of entries.

  *`ELSTBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse the fields of an `ELSTBoxBody`.

  *`ELSTBoxBody.transcribe(self)`*:
  Transcribe an `ELSTBoxBody`.



  A 'free' or 'skip' box - ISO14496 section 8.1.2.
  Note the length and discard the data portion.

  *`FREEBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer, end_offset=Ellipsis, **kw)`*:
  Gather the `padding` field.



  An 'ftyp' File Type box - ISO14496 section 4.3.
  Decode the major_brand, minor_version and compatible_brands.

  *`FTYPBoxBody.compatible_brands`*:
  The compatible brands as a list of 4 byte bytes instances.

  *`FTYPBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer, **kw)`*:
  Gather the `major_brand`, `minor_version` and `brand_bs` fields.



  A common extension of a basic BoxBody, with a version and flags field.
  ISO14496 section 4.2.

  *`FullBoxBody.flags`*:
  The flags value, computed from the 3 flag bytes.



  Parse a `path` string from `offset`.
  Return the path components and the offset where the parse stopped.

  Path components:
  * _identifier_: an identifier represents a `Box` field or if such a
    field is not present, a the first subbox of this type
  * `[`_index_`]`: the subbox with index _index_

  Examples:

      >>> get_deref_path('.abcd[5]')
      (['abcd', 5], 8)



  A HDLRBoxBody is a Handler Reference box - ISO14496 section 8.4.3.

  *`HDLRBoxBody.handler_type`*:
  The handler_type as an ASCII string, its usual form.

  *`HDLRBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `handler_type_long` and `name` fields.



  Attribute name and type for ILST "A of B" schema.



  Apple iTunes Information List, container for iTunes metadata fields.

  The basis of the format knowledge here comes from AtomicParsley's
  documentation here:

      http://atomicparsley.sourceforge.net/mpeg-4files.html

  and additional information from:

      https://github.com/sergiomb2/libmp4v2/wiki/iTunesMetadata

  *`ILSTBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  pylint: disable=attribute-defined-outside-init,too-many-locals
  pylint: disable=too-many-statements,too-many-branches



  Attribute name and type for ILST ISO format schema.



  Attribute name and type for ILST raw schema.



  Attribute name and type for ILST text schema.



  Attribute name and type for ILST `UInt32BE` schema.



  Attribute name and type for ILST `UInt8BE` schema.



  itunes_media_type(type, stik)

  *`itunes_media_type.stik`*:
  Alias for field number 1

  *`itunes_media_type.type`*:
  Alias for field number 0



  itunes_store_country_code(country_name, iso_3166_1_code, itunes_store_code)

  *`itunes_store_country_code.country_name`*:
  Alias for field number 0

  *`itunes_store_country_code.iso_3166_1_code`*:
  Alias for field number 1

  *`itunes_store_country_code.itunes_store_code`*:
  Alias for field number 2



  Command line mode.



  A Media Data Box - ISO14496 section 8.1.1.

  *`MDATBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather all data to the end of the field.

  *`MDATBoxBody.transcribe(self)`*:
  Transcribe the data.
  Raise an `AssertionError` if we skipped the data during the parse.

  *`MDATBoxBody.transcribed_length(self)`*:
  Return the transcription length even if we didn't keep the data.



  A MDHDBoxBody is a Media Header box - ISO14496 section 8.4.2.

  *`MDHDBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `creation_time`, `modification_time`, `timescale`,
  `duration` and `language_short` fields.



  A 'meta' Meta BoxBody - section 8.11.1.

  *`METABoxBody.__getattr__(self, attr)`*:
  Present the `ilst` attributes if present.

  *`METABoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `theHandler` `Box` and gather the following Boxes as `boxes`.



  An 'moov' Movie box - ISO14496 section 8.2.1.
  Decode the contained boxes.



  An 'mvhd' Movie Header box - ISO14496 section 8.2.2.



  A fictitious `Box` encompassing all the Boxes in an input buffer.

  *`OverBox.boxes`*:
  Alias `.value` as `.boxes`: the `Box`es encompassed by this `OverBox`.

  *`OverBox.dump(self, **kw)`*:
  Dump this OverBox.

  *`OverBox.length`*:
  The `OverBox` is as long as the subsidary Boxes.

  *`OverBox.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse the `OverBox`.

  *`OverBox.walk(self)`*:
  Walk the `Box`es in the `OverBox`.

  This does not yield the `OverBox` itself, it isn't really a `Box`.



  Return the `OverBox` from a source (str, int, bytes, file).

  The leading `o` parameter may be one of:
  * `str`: a filesystem file pathname
  * `int`: a OS file descriptor
  * `bytes`: a `bytes` object
  * `file`: if not `int` or `str` the presumption
    is that this is a file-like object

  Keyword arguments are as for `OverBox.from_buffer`.



  Parse an ISO14496 stream from the CornuCopyBuffer `bfr`,
  yield top level OverBoxes.

  Parameters:
  * `bfr`: a `CornuCopyBuffer` provided the stream data,
    preferably seekable
  * `discard_data`: whether to discard unparsed data, default `False`
  * `copy_offsets`: callable to receive `Box` offsets



  Parse the tags from `path`.
  Yield `(box,tags)` for each subbox with tags.

  The optional `tag_prefix` parameter
  may be specified to prefix each tag name with a prefix.
  Other keyword arguments are passed to `parse()`
  (typical example: `discard_data=True`).



  A 'pdin' Progressive Download Information box - ISO14496 section 8.1.3.

  *`PDINBoxBody.PDInfo`*

  *`PDINBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer, **kw)`*:
  Gather the normal version information
  and then the `(rate,initial_delay)` pairs of the data section
  as the `pdinfo` field.



  Report some human friendly information about a box.



  A 'smhd' Sound Media Headerbox - section 12.2.2.

  *`SMHDBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `balance` field.



  A 'stco' Chunk Offset box - section 8.7.5.

  *`STCOBoxBody.chunk_offsets`*:
  Parse the `UInt32BE` chunk offsets from stashed buffer.

  *`STCOBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `entry_count` and `chunk_offsets` fields.



  'stsc' (Sample Table box - section 8.7.4.1.

  *`STSCBoxBody.STSCEntry`*

  *`STSCBoxBody.entries`*:
  A list of `int`s parsed from the `STSCEntry` list.

  *`STSCBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `entry_count` and `entries` fields.



  A 'stsz' Sample Size box - section 8.7.3.2.

  *`STSZBoxBody.entry_sizes`*:
  Parse the `UInt32BE` entry sizes from stashed buffer
  into a list of `int`s.

  *`STSZBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `sample_size`, `sample_count`, and `entry_sizes` fields.

  *`STSZBoxBody.transcribe(self)`*:
  Transcribe the `STSZBoxBody`.



  A 'stz2' Compact Sample Size box - section 8.7.3.3.

  *`STZ2BoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `field_size`, `sample_count` and `entry_sizes` fields.

  *`STZ2BoxBody.transcribe(self)`*:
  transcribe the STZ2BoxBody.



  The 32 bit form of an ISO14496 timestamp.



  The 64 bit form of an ISO14496 timestamp.



  Methods to assist with ISO14496 timestamps.

  *`TimeStampMixin.datetime`*:
  This timestamp as an UTC datetime.

  *`TimeStampMixin.unixtime`*:
  This timestamp as a UNIX time (seconds since 1 January 1970).



  A 'tkhd' Track Header box - ISO14496 section 8.2.2.

  *`TKHDBoxBody.TKHDMatrix`*



  A TrackGroupTypeBoxBody contains a track group id - ISO14496 section 8.3.3.2.

  *`TrackGroupTypeBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `track_group_id` field.



  A TrackReferenceTypeBoxBody contains references to other tracks - ISO14496 section 8.3.3.2.

  *`TrackReferenceTypeBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `track_ids` field.



  Track Reference BoxBody, container for trackReferenceTypeBoxes - ISO14496 section 8.3.3.



  TTSB_Sample(count, delta)

  *`TTSB_Sample.count`*:
  Alias for field number 0

  *`TTSB_Sample.delta`*:
  Alias for field number 1



  An 'url ' Data Entry URL BoxBody - section 8.7.2.1.

  *`URL_BoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `location` field.



  An 'urn ' Data Entry URL BoxBody - section 8.7.2.1.

  *`URN_BoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `name` and `location` fields.



  An ISO14496 UTF8 or UTF16 encoded string.

  *`UTF8or16Field.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather optional BOM and then UTF8 or UTF16 string.

  *`UTF8or16Field.transcribe(self)`*:
  Transcribe the field suitably encoded.



  A 'vmhd' Video Media Headerbox - section 12.1.2.

  *`VMHDBoxBody.OpColor`*

  *`VMHDBoxBody.parse_fields(self, bfr: cs.buffer.CornuCopyBuffer)`*:
  Gather the `graphicsmode` and `opcolor` fields.





  *Release 20241122*:
  Replace many raises of RuntimeError with NotImplementedError, suggestion by @dimaqq on disucss.python.org.

  *Release 20240422*:
  * Replace dropped UTF16NULField with BinaryUTF16NUL.
  * Comment out unused CO64BoxBody.chunk_offsets, uses dropped (and not replaced) deferred_field.
  * Drop FallbackBoxBody, we'll just use BoxBody when there's no box specific subclass.
  * Replace pick_boxbody_class with BoxBody.for_box_type.
  * Rename boxbody_type_from_klass to boxbody_type_from_class.
  * Drop obsolete KNOWN_BOXBODY_CLASSES.
  * MP4Command.cmd_info: print moov.udta.meta.ilst.cover in SIXEL format on a terminal.
  * Rename parse_deref_path to get_deref_path like other lexical functions.
  * ILSTBoxBody.__getattr__: fix lookup of long names.

  *Release 20231129*:
  Small updates and fixes.

  *Release 20230212*:
  * Drop cs.context.StackableState in favour of cs.threads.State.
  * MP4Command.cmd_autotag: use @uses_fstags for the fstags parameter.

  *Release 20220606*:
  Update obsolete use of Tag.with_prefix.

  *Release 20210306*:
  * Huge refactor of the Box classes to the new Binary* classes from cs.binary.
  * mp4: new "tags" subcommand to print the tags parsed from a file.
  * BoxHeader: fix the definition of MAX_BOX_SIZE_32.
  * BoxBody: new parse_boxes utility method to part the remainder of a Box as subBoxes.
  * MP4.cmd_parse: run the main parse in discard_data=True mode.
  * METABoxBody.__getattr__: fix ILST typo.
  * MP4Command: update for new cs.cmdutils.BaseCommand API.
  * Many small fixes and tweaks.

  *Release 20200229*:
  * ILST: recognise @cpy as copyright, sfID as itunes_store_country_code.
  * ILST: new SFID_ISO_3166_1_ALPHA_3_CODE and STIK_MEDIA_TYPES providing context data for various field values, as yet unused.
  * Make various list fields of some boxes deferred because they are expensive to parse (uses new cs.binary deferred_field).
  * add_generic_sample_boxbody: drop __iter__, causes dumb iterators to parse the samples.
  * ILST: iTunes "Date" metadata seem to contain plain years or ISO8601 datestamps.
  * mp4 autotag: add -n (no action) and -p,--prefix (set tag prefix, default 'mp4') options.
  * mp4 autotag: use "mp4." as the tag prefix.

  *Release 20200130*:
  * Parsing of ILST boxes (iTunes metadata).
  * Command line: new "info" subcommand reporting metadata, "autotag" applying metadata to fstags.
  * Box tree walk, ancestor, iteration.
  * Assorted cleanups and internal changes.

  *Release 20190220*:
  parse_buffer yields instead of returns; some small bugfixes.

  *Release 20180810*:
  * parse_fd(): use a mmap to access the descriptor if a regular file and not discard_data;
  * this lets us use the mmapped file as backing store for the data, a big win for the media sections.

  *Release 20180805*:
  Initial PyPI release.


pkgId: dd185886b60be83f59eb8afd60a83bc166074ed11550c8c3f9d024a2dfea23b5
name: python-cs-lex
arch: src
version: 20241119.1
epoch: 0
release: 1
url: https://pypi.org/project/cs-lex/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-lex-20241119.1-1.src.rpm
checksum_type: sha256
summary:
  Lexical analysis functions, tokenisers, transcribers: an arbitrary assortment of lexical and tokenisation functions useful for writing recursive descent parsers, of which I have several. There are also some transcription functions for producing text from various objects, such as `hexify` and `unctrl`.

description:
  Lexical analysis functions, tokenisers, transcribers:
  an arbitrary assortment of lexical and tokenisation functions useful
  for writing recursive descent parsers, of which I have several.
  There are also some transcription functions for producing text
  from various objects, such as `hexify` and `unctrl`.

  *Latest release 20241119.1*:
  Add PyPI classifier, in part to test an updated release script.

  Generally the get_* functions accept a source string and an offset
  (usually optional, default `0`) and return a token and the new offset,
  raising `ValueError` on failed tokenisation.



  Generator yielding complete lines from arbitrary pieces of text from
  the iterable of `str` `chunks`.

  After completion, any remaining newline-free chunks remain
  in the partials list; they will be unavailable to the caller
  unless the list is presupplied.



  Convert a snake cased string `snakecased` into camel case.

  Parameters:
  * `snakecased`: the snake case string to convert
  * `first_letter_only`: optional flag (default `False`);
    if true then just ensure that the first character of a word
    is uppercased, otherwise use `str.title`

  Example:

      >>> camelcase('abc_def')
      'abcDef'
      >>> camelcase('ABc_def')
      'abcDef'
      >>> camelcase('abc_dEf')
      'abcDef'
      >>> camelcase('abc_dEf', first_letter_only=True)
      'abcDEf'



  Return the common prefix of the strings `strs`.

  Examples:

      >>> common_prefix('abc', 'def')
      ''
      >>> common_prefix('abc', 'abd')
      'ab'
      >>> common_prefix('abc', 'abcdef')
      'abc'
      >>> common_prefix('abc', 'abcdef', 'abz')
      'ab'
      >>> # contrast with cs.fileutils.common_path_prefix
      >>> common_prefix('abc/def', 'abc/def1', 'abc/def2')
      'abc/def'



  Return the common suffix of the strings `strs`.



  If the length of `s` exceeds `max_length` (default `32`),
  replace enough of the tail with `ellipsis`
  and the last `roffset` (default `1`) characters of `s`
  to fit in `max_length` characters.



  Compute a cropped `repr()` of `o`.

  Parameters:
  * `o`: the object to represent
  * `max_length`: the maximum length of the representation, default `32`
  * `inner_max_length`: the maximum length of the representations
    of members of `o`, default `max_length//2`
  * `roffset`: the number of trailing characters to preserve, default `1`



  Strip a `prefix` from the front of `s`.
  Return the suffix if `s.startswith(prefix)`, else `s`.

  Example:

      >>> abc_def = 'abc.def'
      >>> cutprefix(abc_def, 'abc.')
      'def'
      >>> cutprefix(abc_def, 'zzz.')
      'abc.def'
      >>> cutprefix(abc_def, '.zzz') is abc_def
      True



  Strip a `suffix` from the end of `s`.
  Return the prefix if `s.endswith(suffix)`, else `s`.

  Example:

      >>> abc_def = 'abc.def'
      >>> cutsuffix(abc_def, '.def')
      'abc'
      >>> cutsuffix(abc_def, '.zzz')
      'abc.def'
      >>> cutsuffix(abc_def, '.zzz') is abc_def
      True



  Formattable `float`.



  Formattable `int`.



  A `FormatableMixin` subclass.

  *`FNumericMixin.localtime(self)`*:
  Treat this as a UNIX timestamp and return a localtime `datetime`.

  *`FNumericMixin.utctime(self)`*:
  Treat this as a UNIX timestamp and return a UTC `datetime`.



  Format the string `format_s` using `Formatter.vformat`,
  return the formatted result.
  This is a wrapper for `str.format_map`
  which raises a more informative `FormatAsError` exception on failure.

  Parameters:
  * `format_s`: the format string to use as the template
  * `format_mapping`: the mapping of available replacement fields
  * `formatter`: an optional `string.Formatter`-like instance
    with a `.vformat(format_string,args,kwargs)` method,
    usually a subclass of `string.Formatter`;
    if not specified then `FormatableFormatter` is used
  * `error_sep`: optional separator for the multipart error message,
    default from `FormatAsError.DEFAULT_SEPARATOR`:
    `'; '`
  * `strict`: optional flag (default `False`)
    indicating that an unresolveable field should raise a
    `KeyError` instead of inserting a placeholder



  A decorator to mark a method as available as a format method.
  Requires the enclosing class to be decorated with `@has_format_attributes`.

  For example,
  the `FormatableMixin.json` method is defined like this:

      @format_attribute
      def json(self):
          return self.FORMAT_JSON_ENCODER.encode(self)

  which allows a `FormatableMixin` subclass instance
  to be used in a format string like this:

      {instance:json}

  to insert a JSON transcription of the instance.

  It is recommended that methods marked with `@format_attribute`
  have no side effects and do not modify state,
  as they are intended for use in ad hoc format strings
  supplied by an end user.



  Escape `{}` characters in a string to protect them from `str.format`.



  Decorator for `__format__` methods which replaces failed formats
  with `{self:format_spec}`.



  A `string.Formatter` subclass interacting with objects
  which inherit from `FormatableMixin`.

  *`FormatableFormatter.format_field(value, format_spec: str)`*:
  Format a value using `value.format_format_field`,
  returning an `FStr`
  (a `str` subclass with additional `format_spec` features).

  We actually recognise colon separated chains of formats
  and apply each format to the previously converted value.
  The final result is promoted to an `FStr` before return.

  *`FormatableFormatter.format_mode`*:
  Thread local state object.

  Attributes:
  * `strict`: initially `False`; raise a `KeyError` for
    unresolveable field names

  *`FormatableFormatter.get_arg_name(field_name)`*:
  Default initial arg_name is an identifier.

  Returns `(prefix,offset)`, and `('',0)` if there is no arg_name.

  *`FormatableFormatter.get_field(self, field_name, args, kwargs)`*:
  Get the object referenced by the field text `field_name`.
  Raises `KeyError` for an unknown `field_name`.

  *`FormatableFormatter.get_format_subspecs(format_spec)`*:
  Parse a `format_spec` as a sequence of colon separated components,
  return a list of the components.

  *`FormatableFormatter.get_subfield(value, subfield_text: str)`*:
  Resolve `value` against `subfield_text`,
  the remaining field text after the term which resolved to `value`.

  For example, a format `{name.blah[0]}`
  has the field text `name.blah[0]`.
  A `get_field` implementation might initially
  resolve `name` to some value,
  leaving `.blah[0]` as the `subfield_text`.
  This method supports taking that value
  and resolving it against the remaining text `.blah[0]`.

  For generality, if `subfield_text` is the empty string
  `value` is returned unchanged.

  *`FormatableFormatter.get_value(self, arg_name, args, kwargs)`*:
  Get the object with index `arg_name`.

  This default implementation returns `(kwargs[arg_name],arg_name)`.



  A subclass of `FormatableFormatter` which  provides 2 features:
  - a `__format__` method which parses the `format_spec` string
    into multiple colon separated terms whose results chain
  - a `format_as` method which formats a format string using `str.format_map`
    with a suitable mapping derived from the instance
    via its `format_kwargs` method
    (whose default is to return the instance itself)

  The `format_as` method is like an inside out `str.format` or
  `object.__format__` method.

  The `str.format` method is designed for formatting a string
  from a variety of other objects supplied in the keyword arguments.

  The `object.__format__` method is for filling out a single `str.format`
  replacement field from a single object.

  By contrast, `format_as` is designed to fill out an entire format
  string from the current object.

  For example, the `cs.tagset.TagSetMixin` class
  uses `FormatableMixin` to provide a `format_as` method
  whose replacement fields are derived from the tags in the tag set.

  Subclasses wanting to provide additional `format_spec` terms
  should:
  - override `FormatableFormatter.format_field1` to implement
    terms with no colons, letting `format_field` do the split into terms
  - override `FormatableFormatter.get_format_subspecs` to implement
    the parse of `format_spec` into a sequence of terms.
    This might recognise a special additional syntax
    and quietly fall back to `super().get_format_subspecs`
    if that is not present.

  *`FormatableMixin.__format__(self, format_spec)`*:
  Format `self` according to `format_spec`.

  This implementation calls `self.format_field`.
  As such, a `format_spec` is considered
  a sequence of colon separated terms.

  Classes wanting to implement additional format string syntaxes
  should either:
  - override `FormatableFormatter.format_field1` to implement
    terms with no colons, letting `format_field1` do the split into terms
  - override `FormatableFormatter.get_format_subspecs` to implement
    the term parse.

  The default implementation of `__format1__` just calls `super().__format__`.
  Implementations providing specialised formats
  should implement them in `__format1__`
  with fallback to `super().__format1__`.

  *`FormatableMixin.convert_field(self, value, conversion)`*:
  The default converter for fields calls `Formatter.convert_field`.

  *`FormatableMixin.convert_via_method_or_attr(self, value, format_spec)`*:
  Apply a method or attribute name based conversion to `value`
  where `format_spec` starts with a method name
  applicable to `value`.
  Return `(converted,offset)`
  being the converted value and the offset after the method name.

  Note that if there is not a leading identifier on `format_spec`
  then `value` is returned unchanged with `offset=0`.

  The methods/attributes are looked up in the mapping
  returned by `.format_attributes()` which represents allowed methods
  (broadly, one should not allow methods which modify any state).

  If this returns a callable, it is called to obtain the converted value
  otherwise it is used as is.

  As a final tweak,
  if `value.get_format_attribute()` raises an `AttributeError`
  (the attribute is not an allowed attribute)
  or calling the attribute raises a `TypeError`
  (the `value` isn't suitable)
  and the `value` is not an instance of `FStr`,
  convert it to an `FStr` and try again.
  This provides the common utility methods on other types.

  The motivating example was a `PurePosixPath`,
  which does not JSON transcribe;
  this tweak supports both
  `posixpath:basename` via the pathlib stuff
  and `posixpath:json` via `FStr`
  even though a `PurePosixPath` does not subclass `FStr`.

  *`FormatableMixin.format_as(self, format_s, error_sep=None, strict=None, **control_kw)`*:
  Return the string `format_s` formatted using the mapping
  returned by `self.format_kwargs(**control_kw)`.

  If a class using the mixin has no `format_kwargs(**control_kw)` method
  to provide a mapping for `str.format_map`
  then the instance itself is used as the mapping.

  *`FormatableMixin.get_format_attribute(self, attr)`*:
  Return a mapping of permitted methods to functions of an instance.
  This is used to whitelist allowed `:`*name* method formats
  to prevent scenarios like little Bobby Tables calling `delete()`.

  *`FormatableMixin.get_format_attributes()`*:
  Return the mapping of format attributes.

  *`FormatableMixin.json(self)`*:
  The value transcribed as compact JSON.



  Subclass of `LookupError` for use by `format_as`.



  A `str` subclass with the `FormatableMixin` methods,
  particularly its `__format__` method
  which uses `str` method names as valid formats.

  It also has a bunch of utility methods which are available
  as `:`*method* in format strings.

  *`FStr.basename(self)`*:
  Treat as a filesystem path and return the basename.

  *`FStr.dirname(self)`*:
  Treat as a filesystem path and return the dirname.

  *`FStr.f(self)`*:
  Parse `self` as a `float`.

  *`FStr.i(self, base=10)`*:
  Parse `self` as an `int`.

  *`FStr.lc(self)`*:
  Lowercase using `lc_()`.

  *`FStr.path(self)`*:
  Convert to a native filesystem `pathlib.Path`.

  *`FStr.posix_path(self)`*:
  Convert to a Posix filesystem `pathlib.Path`.

  *`FStr.windows_path(self)`*:
  Convert to a Windows filesystem `pathlib.Path`.



  Scan the string `s` for characters in `gochars` starting at `offset`.
  Return `(match,new_offset)`.

  `gochars` may also be a callable, in which case a character
  `ch` is accepted if `gochars(ch)` is true.



  Scan the string `s` for decimal characters starting at `offset` (default `0`).
  Return `(dec_string,new_offset)`.



  Fetch a decimal or basic float (nnn.nnn) value
  from the str `s` at `offset` (default `0`).
  Return `(value,new_offset)`.



  Scan the string `s` for a decimal value starting at `offset` (default `0`).
  Return `(value,new_offset)`.



  Collect text from the string `s` from position `offset` up
  to the first occurence of delimiter `delim`; return the text
  excluding the delimiter and the offset after the delimiter.



  Scan the string `s` for a dotted identifier (by default an
  ASCII letter or underscore followed by letters, digits or
  underscores) with optional trailing dot and another dotted
  identifier, starting at `offset` (default `0`).
  Return `(match,new_offset)`.

  Note: the empty string and an unchanged offset will be returned if
  there is no leading letter/underscore.

  Keyword arguments are passed to `get_identifier`
  (used for each component of the dotted identifier).



  Parse a simple environment variable reference to $varname or
  $x where "x" is a special character.

  Parameters:
  * `s`: the string with the variable reference
  * `offset`: the starting point for the reference
  * `default`: default value for missing environment variables;
     if `None` (the default) a `ValueError` is raised
  * `environ`: the environment mapping, default `os.environ`
  * `specials`: the mapping of special single character variables



  Scan the string `s` for hexadecimal characters starting at `offset` (default `0`).
  Return `(hex_string,new_offset)`.



  Scan the string `s` for a hexadecimal value starting at `offset` (default `0`).
  Return `(value,new_offset)`.



  Scan the string `s` for an identifier (by default an ASCII
  letter or underscore followed by letters, digits or underscores)
  starting at `offset` (default 0).
  Return `(match,new_offset)`.

  *Note*: the empty string and an unchanged offset will be returned if
  there is no leading letter/underscore.

  Parameters:
  * `s`: the string to scan
  * `offset`: the starting offset, default `0`.
  * `alpha`: the characters considered alphabetic,
    default `string.ascii_letters`.
  * `number`: the characters considered numeric,
    default `string.digits`.
  * `extras`: extra characters considered part of an identifier,
    default `'_'`.



  Parse a `[`*clausename*`]`*entryname* string
  from `s` at `offset` (default `0`).
  Return `(clausename,entryname,new_offset)`.



  Parse a `[`*clausename*`]` string from `s` at `offset` (default `0`).
  Return `(clausename,new_offset)`.



  Scan the string `s` for characters not in `string.whitespace`
  starting at `offset` (default `0`).
  Return `(match,new_offset)`.



  Scan the string `s` for characters not in `stopchars` starting
  at `offset` (default `0`).
  Return `(match,new_offset)`.



  Strip a leading `prefix` and numeric value `n` from the string `s`
  starting at `offset` (default `0`).
  Return the matched prefix, the numeric value and the new offset.
  Returns `(None,None,offset)` on no match.

  Parameters:
  * `s`: the string to parse
  * `prefix`: the prefix string which must appear at `offset`
    or an object with a `match(str,offset)` method
    such as an `re.Pattern` regexp instance
  * `n`: optional integer value;
    if omitted any value will be accepted, otherwise the numeric
    part must match `n`

  If `prefix` is a `str`, the "matched prefix" return value is `prefix`.
  Otherwise the "matched prefix" return value is the result of
  the `prefix.match(s,offset)` call. The result must also support
  a `.end()` method returning the offset in `s` beyond the match,
  used to locate the following numeric portion.

  Examples:

     >>> import re
     >>> get_prefix_n('s03e01--', 's')
     ('s', 3, 3)
     >>> get_prefix_n('s03e01--', 's', 3)
     ('s', 3, 3)
     >>> get_prefix_n('s03e01--', 's', 4)
     (None, None, 0)
     >>> get_prefix_n('s03e01--', re.compile('[es]',re.I))
     (<re.Match object; span=(0, 1), match='s'>, 3, 3)
     >>> get_prefix_n('s03e01--', re.compile('[es]',re.I), offset=3)
     (<re.Match object; span=(3, 4), match='e'>, 1, 6)



  Get quoted text with slosh escapes and optional environment substitution.

  Parameters:
  * `s`: the string containg the quoted text.
  * `offset`: the starting point, default `0`.
  * `q`: the quote character, default `'"'`. If `q` is `None`,
    do not expect the string to be delimited by quote marks.
  * `environ`: if not `None`, also parse and expand `$`*envvar* references.
  * `default`: passed to `get_envvar`



  Parse a double quoted string or an identifier.



  Collect slosh escaped text from the string `s` from position
  `offset` (default `0`) and return the decoded unicode string and
  the offset of the completed parse.

  Parameters:
  * `delim`: end of string delimiter, such as a single or double quote.
  * `offset`: starting offset within `s`, default `0`.
  * `slosh`: escape character, default a slosh ('\').
  * `mapper`: a mapping function which accepts a single character
    and returns a replacement string or `None`; this is used the
    replace things such as '\t' or '\n'. The default is the
    `slosh_mapper` function, whose default mapping is `SLOSH_CHARMAP`.
  * `specials`: a mapping of other special character sequences and parse
    functions for gathering them up. When one of the special
    character sequences is found in the string, the parse
    function is called to parse at that point.
    The parse functions accept
    `s` and the offset of the special character. They return
    the decoded string and the offset past the parse.

  The escape character `slosh` introduces an encoding of some
  replacement text whose value depends on the following character.
  If the following character is:
  * the escape character `slosh`, insert the escape character.
  * the string delimiter `delim`, insert the delimiter.
  * the character 'x', insert the character with code from the following
    2 hexadecimal digits.
  * the character 'u', insert the character with code from the following
    4 hexadecimal digits.
  * the character 'U', insert the character with code from the following
    8 hexadecimal digits.
  * a character from the keys of `mapper`



  Strip a trailing "part N" suffix from the string `s`.
  Return the matched suffix and the number part number.
  Retrn `(None,None)` on no match.

  Parameters:
  * `s`: the string
  * `keywords`: an iterable of `str` to match, or a single `str`;
    default `'part'`
  * `numeral_map`: an optional mapping of numeral names to numeric values;
    default `NUMERAL_NAMES['en']`, the English numerals

  Exanmple:

      >>> get_suffix_part('s09e10 - A New World: Part One')
      (': Part One', 1)



  Parse the string `s` from position `offset` using the supplied
  tokeniser functions `getters`.
  Return the list of tokens matched and the final offset.

  Parameters:
  * `s`: the string to parse.
  * `offset`: the starting position for the parse.
  * `getters`: an iterable of tokeniser specifications.

  Each tokeniser specification `getter` is either:
  * a callable expecting `(s,offset)` and returning `(token,new_offset)`
  * a literal string, to be matched exactly
  * a `tuple` or `list` with values `(func,args,kwargs)`;
    call `func(s,offset,*args,**kwargs)`
  * an object with a `.match` method such as a regex;
    call `getter.match(s,offset)` and return a match object with
    a `.end()` method returning the offset of the end of the match



  Scan the string `s` for an identifier as for `get_identifier`,
  but require the letters to be uppercase.



  Scan the string `s` for characters in `string.whitespace`
  starting at `offset` (default `0`).
  Return `(match,new_offset)`.



  Class decorator to walk this class for direct methods
  marked as for use in format strings
  and to include them in `cls.format_attributes()`.

  Methods are normally marked with the `@format_attribute` decorator.

  If `inherit` is true the base format attributes will be
  obtained from other classes:
  * `inherit` is `True`: use `cls.__mro__`
  * `inherit` is a class: use that class
  * otherwise assume `inherit` is an iterable of classes
  For each class `otherclass`, update the initial attribute
  mapping from `otherclass.get_format_attributes()`.



  A flavour of `binascii.hexlify` returning a `str`.



  Convert a string for safe transcription in HTML.

  Parameters:
  * `s`: the string
  * `nbsp`: replaces spaces with `"&nbsp;"` to prevent word folding,
    default `False`.



  Quote a string for use in HTML.



  Return the `paragraph` indented by `line_indent` (default `"  "`).



  Test if the string `s` is an identifier from position `offset` onward.



  Test if the string `s` is an identifier
  from position `offset` (default `0`) onward.



  Test if the string `s` is an uppercase identifier
  from position `offset` (default `0`) onward.



  Check that a string matches the regular expression `^[A-Z][A-Z_0-9]*$`.



  Quote a string for use in JavaScript.



  Return `value.lower()`
  with `'-'` translated into `'_'` and `' '` translated into `'-'`.

  I use this to construct lowercase filenames containing a
  readable transcription of a title string.

  See also `titleify_lc()`, an imperfect reversal of this.



  Wrapper for `get_tokens` which catches `ValueError` exceptions
  and returns `(None,offset)`.



  Take an attribute name `attr` and return `(key,is_plural)`.

  Examples:
  * `'FOO'` returns `('FOO',False)`.
  * `'FOOs'` or `'FOOes'` returns `('FOO',True)`.
  Otherwise return `(None,False)`.



  Quote a string for use in PHP code.



  Like `typed_str` but using `repr` instead of `str`.
  This is available as both `typed_repr` and `r`.



  Return "type(o).__name__:str(o)" for some object `o`.
  This is available as both `typed_str` and `s`.

  Parameters:
  * `use_cls`: default `False`;
    if true, use `str(type(o))` instead of `type(o).__name__`
  * `use_repr`: default `False`;
    if true, use `repr(o)` instead of `str(o)`

  I use this a lot when debugging. Example:

      from cs.lex import typed_str as s
      ......
      X("foo = %s", s(foo))



  Convenience routine for skipping past whitespace;
  returns the offset of the next nonwhitespace character.



  Return a string to replace backslash-`c`, or `None`.



  Convert a camel cased string `camelcased` into snake case.

  Parameters:
  * `cameelcased`: the cameel case string to convert
  * `first_letter_only`: optional flag (default `False`);
    if true then just ensure that the first character of a word
    is uppercased, otherwise use `str.title`

  Example:

      >>> snakecase('abcDef')
      'abc_def'
      >>> snakecase('abcDEf')
      'abc_def'
      >>> snakecase('AbcDef')
      'abc_def'



  Split a path with an optional leading `[user@]rhost:` prefix
  into the prefix and the remaining path.
  `None` is returned for the prefix is there is none.
  This is useful for things like `rsync` targets etc.



  Slightly smarter dedent which ignores a string's opening indent.

  Algorithm:
  strip the supplied string `s`, pull off the leading line,
  dedent the rest, put back the leading line.

  This supports my preferred docstring layout, where the opening
  line of text is on the same line as the opening quote.

  The optional `post_indent` parameter may be used to indent
  the dedented text before return.

  The optional `sub_indent` parameter may be used to indent
  the second and following lines if the dedented text before return.

  Examples:

      >>> def func(s):
      ...   """ Slightly smarter dedent which ignores a string's opening indent.
      ...       Strip the supplied string `s`. Pull off the leading line.
      ...       Dedent the rest. Put back the leading line.
      ...   """
      ...   pass
      ...
      >>> from cs.lex import stripped_dedent
      >>> print(stripped_dedent(func.__doc__))
      Slightly smarter dedent which ignores a string's opening indent.
      Strip the supplied string `s`. Pull off the leading line.
      Dedent the rest. Put back the leading line.
      >>> print(stripped_dedent(func.__doc__, sub_indent='  '))
      Slightly smarter dedent which ignores a string's opening indent.
        Strip the supplied string `s`. Pull off the leading line.
        Dedent the rest. Put back the leading line.
      >>> print(stripped_dedent(func.__doc__, post_indent='  '))
        Slightly smarter dedent which ignores a string's opening indent.
        Strip the supplied string `s`. Pull off the leading line.
        Dedent the rest. Put back the leading line.
      >>> print(stripped_dedent(func.__doc__, post_indent='  ', sub_indent='| '))
        Slightly smarter dedent which ignores a string's opening indent.
        | Strip the supplied string `s`. Pull off the leading line.
        | Dedent the rest. Put back the leading line.



  Convert an iterable to strings and join with `sep` (default `', '`).



  Compute some spaces to use a tab padding at an offfset.



  A generator yielding lines of values from `rows` aligned in columns.



  Transcribe the bytes `bs` to text using compact text runs for
  some common text values.

  This can be reversed with the `untexthexify` function.

  This is an ad doc format devised to be compact but also to
  expose "text" embedded within to the eye. The original use
  case was transcribing a binary directory entry format, where
  the filename parts would be somewhat visible in the transcription.

  The output is a string of hexadecimal digits for the encoded
  bytes except for runs of values from the whitelist, which are
  enclosed in the shiftin and shiftout markers and transcribed
  as is. The default whitelist is values of the ASCII letters,
  the decimal digits and the punctuation characters '_-+.,'.
  The default shiftin and shiftout markers are '[' and ']'.

  String objects converted with either `hexify` and `texthexify`
  output strings may be freely concatenated and decoded with
  `untexthexify`.

  Example:

      >>> texthexify(b'&^%&^%abcdefghi)(*)(*')
      '265e25265e25[abcdefghi]29282a29282a'

  Parameters:
  * `bs`: the bytes to transcribe
  * `shiftin`: Optional. The marker string used to indicate a shift to
    direct textual transcription of the bytes, default: `'['`.
  * `shiftout`: Optional. The marker string used to indicate a
    shift from text mode back into hexadecimal transcription,
    default `']'`.
  * `whitelist`: an optional bytes or string object indicating byte
    values which may be represented directly in text;
    the default value is the ASCII letters, the decimal digits
    and the punctuation characters `'_-+.,'`.



  Translate `'-'` into `' '` and `'_'` translated into `'-'`,
  then titlecased.

  See also `lc_()`, which this reverses imperfectly.



  Like `typed_str` but using `repr` instead of `str`.
  This is available as both `typed_repr` and `r`.



  Return "type(o).__name__:str(o)" for some object `o`.
  This is available as both `typed_str` and `s`.

  Parameters:
  * `use_cls`: default `False`;
    if true, use `str(type(o))` instead of `type(o).__name__`
  * `use_repr`: default `False`;
    if true, use `repr(o)` instead of `str(o)`

  I use this a lot when debugging. Example:

      from cs.lex import typed_str as s
      ......
      X("foo = %s", s(foo))



  Return the string `s` with `TAB`s expanded and control characters
  replaced with printable representations.



  Decode a textual representation of binary data into binary data.

  This is the reverse of the `texthexify` function.

  Outside of the `shiftin`/`shiftout` markers the binary data
  are represented as hexadecimal. Within the markers the bytes
  have the values of the ordinals of the characters.

  Example:

      >>> untexthexify('265e25265e25[abcdefghi]29282a29282a')
      b'&^%&^%abcdefghi)(*)(*'

  Parameters:
  * `s`: the string containing the text representation.
  * `shiftin`: Optional. The marker string commencing a sequence
    of direct text transcription, default `'['`.
  * `shiftout`: Optional. The marker string ending a sequence
    of direct text transcription, default `']'`.





  *Release 20241119.1*:
  Add PyPI classifier, in part to test an updated release script.

  *Release 20241119*:
  stripped_dedent: new optional sub_indent parameter for indenting the second and following lines, handy for usage messages.

  *Release 20241109*:
  * stripped_dedent: new optional post_indent parameter to indent the dedented text.
  * New tabulate(*rows) generator function yielding lines of padded columns.

  *Release 20240630*:
  New indent(paragraph,line_indent="  ") function.

  *Release 20240519*:
  New get_suffix_part() to extract things line ": Part One" from something such as a TV episode name.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240211*:
  New split_remote_path() function to recognise [[user@]host]:path.

  *Release 20231018*:
  New is_uc_identifier function.

  *Release 20230401*:
  Import update.

  *Release 20230217.1*:
  Fix package requirements.

  *Release 20230217*:
  * New get_prefix_n function to parse a numeric value preceeded by a prefix.
  * Drop strip_prefix_n, get_prefix_n is more general and I had not got around to using strip_prefix_n yet - when I did, I ended up writing get_prefix_n.

  *Release 20230210*:
  * @has_format_attributes: new optional inherit parameter to inherit superclass (or other) format attributes, default False.
  * New FNumericMixin, FFloat, FInt FormatableMixin subclasses like FStr - they add .localtime and .utctime formattable attributes.

  *Release 20220918*:
  typed_str(): crop the value part, default max_length=32, bugfix message cropping.

  *Release 20220626*:
  * Remove dependency on cs.py3, we've been Python 2 incompatible for a while.
  * FormatableFormatter.format_field: promote None to FStr(None).

  *Release 20220227*:
  * typed_str,typed_repr: make max_length the first optional positional parameter, make other parameters keyword only.
  * New camelcase() and snakecase() functions.

  *Release 20211208*:
  Docstring updates.

  *Release 20210913*:
  * FormatableFormatter.FORMAT_RE_ARG_NAME_s: strings commencing with digits now match \d+(\.\d+)[a-z]+, eg "02d".
  * Alias typed_str as s and typed_repr as r.
  * FormatableFormatter: new .format_mode thread local state object initially with strict=False, used to control whether unknown fields leave a placeholder or raise KeyError.
  * FormatableFormatter.format_field: assorted fixes.

  *Release 20210906*:
  New strip_prefix_n() function to strip a leading `prefix` and numeric value `n` from the start of a string.

  *Release 20210717*:
  * Many many changes to FormatableMixin, FormatableFormatter and friends around supporting {foo|conv1|con2|...} instead of {foo!conv}. Still in flux.
  * New typed_repr like typed_str but using repr.

  *Release 20210306*:
  * New cropped() function to crop strings.
  * Rework cropped_repr() to do the repr() itself, and to crop the interiors of tuples and lists.
  * cropped_repr: new inner_max_length for cropping the members of collections.
  * cropped_repr: special case for length=1 tuples.
  * New typed_str(o) object returning type(o).__name__:str(o) in the default case, useful for debugging.

  *Release 20201228*:
  Minor doc updates.

  *Release 20200914*:
  * Hide terribly special purpose lastlinelen() in cs.hier under a private name.
  * New common_prefix and common_suffix function to compare strings.

  *Release 20200718*:
  get_chars: accept a callable for gochars, indicating a per character test function.

  *Release 20200613*:
  cropped_repr: replace hardwired 29 with computed length

  *Release 20200517*:
  * New get_ini_clausename to parse "[clausename]".
  * New get_ini_clause_entryname parsing "[clausename]entryname".
  * New cropped_repr for returning a shortened repr()+"..." if the length exceeds a threshold.
  * New format_escape function to double {} characters to survive str.format.

  *Release 20200318*:
  * New lc_() function to lowercase and dash a string, new titleify_lc() to mostly reverse lc_().
  * New format_as function, FormatableMixin and related FormatAsError.

  *Release 20200229*:
  New cutprefix and cutsuffix functions.

  *Release 20190812*:
  Fix bad slosh escapes in strings.

  *Release 20190220*:
  New function get_qstr_or_identifier.

  *Release 20181108*:
  new function get_decimal_or_float_value to read a decimal or basic float

  *Release 20180815*:
  No semantic changes; update some docstrings and clean some lint, fix a unit test.

  *Release 20180810*:
  * New get_decimal_value and get_hexadecimal_value functions.
  * New stripped_dedent function, a slightly smarter textwrap.dedent.

  *Release 20171231*:
  New function get_decimal. Drop unused function dict2js.

  *Release 20170904*:
  Python 2/3 ports, move rfc2047 into new cs.rfc2047 module.

  *Release 20160828*:
  * Use "install_requires" instead of "requires" in DISTINFO.
  * Discard str1(), pointless optimisation.
  * unrfc2047: map _ to SPACE, improve exception handling.
  * Add phpquote: quote a string for use in PHP code; add docstring to jsquote.
  * Add is_identifier test.
  * Add get_dotted_identifier.
  * Add is_dotted_identifier.
  * Add get_hexadecimal.
  * Add skipwhite, convenince wrapper for get_white returning just the next offset.
  * Assorted bugfixes and improvements.

  *Release 20150120*:
  cs.lex: texthexify: backport to python 2 using cs.py3 bytes type

  *Release 20150118*:
  metadata updates

  *Release 20150116*:
  PyPI metadata and slight code cleanup.


pkgId: 6cc21413113354a9b3ecfe3478eb0269235a01c1118b1dce045017a0c542a79f
name: python-cs-lex
arch: src
version: 20241207
epoch: 0
release: 1
url: https://pypi.org/project/cs-lex/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-lex-20241207-1.src.rpm
checksum_type: sha256
summary:
  Lexical analysis functions, tokenisers, transcribers: an arbitrary assortment of lexical and tokenisation functions useful for writing recursive descent parsers, of which I have several. There are also some transcription functions for producing text from various objects, such as `hexify` and `unctrl`.

description:
  Lexical analysis functions, tokenisers, transcribers:
  an arbitrary assortment of lexical and tokenisation functions useful
  for writing recursive descent parsers, of which I have several.
  There are also some transcription functions for producing text
  from various objects, such as `hexify` and `unctrl`.

  *Latest release 20241207*:
  tabulate: split cells containing newlines over multiple output rows.

  Generally the get_* functions accept a source string and an offset
  (usually optional, default `0`) and return a token and the new offset,
  raising `ValueError` on failed tokenisation.



  Generator yielding complete lines from arbitrary pieces of text from
  the iterable of `str` `chunks`.

  After completion, any remaining newline-free chunks remain
  in the partials list; they will be unavailable to the caller
  unless the list is presupplied.



  Convert a snake cased string `snakecased` into camel case.

  Parameters:
  * `snakecased`: the snake case string to convert
  * `first_letter_only`: optional flag (default `False`);
    if true then just ensure that the first character of a word
    is uppercased, otherwise use `str.title`

  Example:

      >>> camelcase('abc_def')
      'abcDef'
      >>> camelcase('ABc_def')
      'abcDef'
      >>> camelcase('abc_dEf')
      'abcDef'
      >>> camelcase('abc_dEf', first_letter_only=True)
      'abcDEf'



  Return the common prefix of the strings `strs`.

  Examples:

      >>> common_prefix('abc', 'def')
      ''
      >>> common_prefix('abc', 'abd')
      'ab'
      >>> common_prefix('abc', 'abcdef')
      'abc'
      >>> common_prefix('abc', 'abcdef', 'abz')
      'ab'
      >>> # contrast with cs.fileutils.common_path_prefix
      >>> common_prefix('abc/def', 'abc/def1', 'abc/def2')
      'abc/def'



  Return the common suffix of the strings `strs`.



  If the length of `s` exceeds `max_length` (default `32`),
  replace enough of the tail with `ellipsis`
  and the last `roffset` (default `1`) characters of `s`
  to fit in `max_length` characters.



  Compute a cropped `repr()` of `o`.

  Parameters:
  * `o`: the object to represent
  * `max_length`: the maximum length of the representation, default `32`
  * `inner_max_length`: the maximum length of the representations
    of members of `o`, default `max_length//2`
  * `roffset`: the number of trailing characters to preserve, default `1`



  Strip a `prefix` from the front of `s`.
  Return the suffix if `s.startswith(prefix)`, else `s`.

  Example:

      >>> abc_def = 'abc.def'
      >>> cutprefix(abc_def, 'abc.')
      'def'
      >>> cutprefix(abc_def, 'zzz.')
      'abc.def'
      >>> cutprefix(abc_def, '.zzz') is abc_def
      True



  Strip a `suffix` from the end of `s`.
  Return the prefix if `s.endswith(suffix)`, else `s`.

  Example:

      >>> abc_def = 'abc.def'
      >>> cutsuffix(abc_def, '.def')
      'abc'
      >>> cutsuffix(abc_def, '.zzz')
      'abc.def'
      >>> cutsuffix(abc_def, '.zzz') is abc_def
      True



  Formattable `float`.



  Formattable `int`.



  A `FormatableMixin` subclass.

  *`FNumericMixin.localtime(self)`*:
  Treat this as a UNIX timestamp and return a localtime `datetime`.

  *`FNumericMixin.utctime(self)`*:
  Treat this as a UNIX timestamp and return a UTC `datetime`.



  Format the string `format_s` using `Formatter.vformat`,
  return the formatted result.
  This is a wrapper for `str.format_map`
  which raises a more informative `FormatAsError` exception on failure.

  Parameters:
  * `format_s`: the format string to use as the template
  * `format_mapping`: the mapping of available replacement fields
  * `formatter`: an optional `string.Formatter`-like instance
    with a `.vformat(format_string,args,kwargs)` method,
    usually a subclass of `string.Formatter`;
    if not specified then `FormatableFormatter` is used
  * `error_sep`: optional separator for the multipart error message,
    default from `FormatAsError.DEFAULT_SEPARATOR`:
    `'; '`
  * `strict`: optional flag (default `False`)
    indicating that an unresolveable field should raise a
    `KeyError` instead of inserting a placeholder



  A decorator to mark a method as available as a format method.
  Requires the enclosing class to be decorated with `@has_format_attributes`.

  For example,
  the `FormatableMixin.json` method is defined like this:

      @format_attribute
      def json(self):
          return self.FORMAT_JSON_ENCODER.encode(self)

  which allows a `FormatableMixin` subclass instance
  to be used in a format string like this:

      {instance:json}

  to insert a JSON transcription of the instance.

  It is recommended that methods marked with `@format_attribute`
  have no side effects and do not modify state,
  as they are intended for use in ad hoc format strings
  supplied by an end user.



  Escape `{}` characters in a string to protect them from `str.format`.



  Decorator for `__format__` methods which replaces failed formats
  with `{self:format_spec}`.



  A `string.Formatter` subclass interacting with objects
  which inherit from `FormatableMixin`.

  *`FormatableFormatter.format_field(value, format_spec: str)`*:
  Format a value using `value.format_format_field`,
  returning an `FStr`
  (a `str` subclass with additional `format_spec` features).

  We actually recognise colon separated chains of formats
  and apply each format to the previously converted value.
  The final result is promoted to an `FStr` before return.

  *`FormatableFormatter.format_mode`*:
  Thread local state object.

  Attributes:
  * `strict`: initially `False`; raise a `KeyError` for
    unresolveable field names

  *`FormatableFormatter.get_arg_name(field_name)`*:
  Default initial arg_name is an identifier.

  Returns `(prefix,offset)`, and `('',0)` if there is no arg_name.

  *`FormatableFormatter.get_field(self, field_name, args, kwargs)`*:
  Get the object referenced by the field text `field_name`.
  Raises `KeyError` for an unknown `field_name`.

  *`FormatableFormatter.get_format_subspecs(format_spec)`*:
  Parse a `format_spec` as a sequence of colon separated components,
  return a list of the components.

  *`FormatableFormatter.get_subfield(value, subfield_text: str)`*:
  Resolve `value` against `subfield_text`,
  the remaining field text after the term which resolved to `value`.

  For example, a format `{name.blah[0]}`
  has the field text `name.blah[0]`.
  A `get_field` implementation might initially
  resolve `name` to some value,
  leaving `.blah[0]` as the `subfield_text`.
  This method supports taking that value
  and resolving it against the remaining text `.blah[0]`.

  For generality, if `subfield_text` is the empty string
  `value` is returned unchanged.

  *`FormatableFormatter.get_value(self, arg_name, args, kwargs)`*:
  Get the object with index `arg_name`.

  This default implementation returns `(kwargs[arg_name],arg_name)`.



  A subclass of `FormatableFormatter` which  provides 2 features:
  - a `__format__` method which parses the `format_spec` string
    into multiple colon separated terms whose results chain
  - a `format_as` method which formats a format string using `str.format_map`
    with a suitable mapping derived from the instance
    via its `format_kwargs` method
    (whose default is to return the instance itself)

  The `format_as` method is like an inside out `str.format` or
  `object.__format__` method.

  The `str.format` method is designed for formatting a string
  from a variety of other objects supplied in the keyword arguments.

  The `object.__format__` method is for filling out a single `str.format`
  replacement field from a single object.

  By contrast, `format_as` is designed to fill out an entire format
  string from the current object.

  For example, the `cs.tagset.TagSetMixin` class
  uses `FormatableMixin` to provide a `format_as` method
  whose replacement fields are derived from the tags in the tag set.

  Subclasses wanting to provide additional `format_spec` terms
  should:
  - override `FormatableFormatter.format_field1` to implement
    terms with no colons, letting `format_field` do the split into terms
  - override `FormatableFormatter.get_format_subspecs` to implement
    the parse of `format_spec` into a sequence of terms.
    This might recognise a special additional syntax
    and quietly fall back to `super().get_format_subspecs`
    if that is not present.

  *`FormatableMixin.__format__(self, format_spec)`*:
  Format `self` according to `format_spec`.

  This implementation calls `self.format_field`.
  As such, a `format_spec` is considered
  a sequence of colon separated terms.

  Classes wanting to implement additional format string syntaxes
  should either:
  - override `FormatableFormatter.format_field1` to implement
    terms with no colons, letting `format_field1` do the split into terms
  - override `FormatableFormatter.get_format_subspecs` to implement
    the term parse.

  The default implementation of `__format1__` just calls `super().__format__`.
  Implementations providing specialised formats
  should implement them in `__format1__`
  with fallback to `super().__format1__`.

  *`FormatableMixin.convert_field(self, value, conversion)`*:
  The default converter for fields calls `Formatter.convert_field`.

  *`FormatableMixin.convert_via_method_or_attr(self, value, format_spec)`*:
  Apply a method or attribute name based conversion to `value`
  where `format_spec` starts with a method name
  applicable to `value`.
  Return `(converted,offset)`
  being the converted value and the offset after the method name.

  Note that if there is not a leading identifier on `format_spec`
  then `value` is returned unchanged with `offset=0`.

  The methods/attributes are looked up in the mapping
  returned by `.format_attributes()` which represents allowed methods
  (broadly, one should not allow methods which modify any state).

  If this returns a callable, it is called to obtain the converted value
  otherwise it is used as is.

  As a final tweak,
  if `value.get_format_attribute()` raises an `AttributeError`
  (the attribute is not an allowed attribute)
  or calling the attribute raises a `TypeError`
  (the `value` isn't suitable)
  and the `value` is not an instance of `FStr`,
  convert it to an `FStr` and try again.
  This provides the common utility methods on other types.

  The motivating example was a `PurePosixPath`,
  which does not JSON transcribe;
  this tweak supports both
  `posixpath:basename` via the pathlib stuff
  and `posixpath:json` via `FStr`
  even though a `PurePosixPath` does not subclass `FStr`.

  *`FormatableMixin.format_as(self, format_s, error_sep=None, strict=None, **control_kw)`*:
  Return the string `format_s` formatted using the mapping
  returned by `self.format_kwargs(**control_kw)`.

  If a class using the mixin has no `format_kwargs(**control_kw)` method
  to provide a mapping for `str.format_map`
  then the instance itself is used as the mapping.

  *`FormatableMixin.get_format_attribute(self, attr)`*:
  Return a mapping of permitted methods to functions of an instance.
  This is used to whitelist allowed `:`*name* method formats
  to prevent scenarios like little Bobby Tables calling `delete()`.

  *`FormatableMixin.get_format_attributes()`*:
  Return the mapping of format attributes.

  *`FormatableMixin.json(self)`*:
  The value transcribed as compact JSON.



  Subclass of `LookupError` for use by `format_as`.



  A `str` subclass with the `FormatableMixin` methods,
  particularly its `__format__` method
  which uses `str` method names as valid formats.

  It also has a bunch of utility methods which are available
  as `:`*method* in format strings.

  *`FStr.basename(self)`*:
  Treat as a filesystem path and return the basename.

  *`FStr.dirname(self)`*:
  Treat as a filesystem path and return the dirname.

  *`FStr.f(self)`*:
  Parse `self` as a `float`.

  *`FStr.i(self, base=10)`*:
  Parse `self` as an `int`.

  *`FStr.lc(self)`*:
  Lowercase using `lc_()`.

  *`FStr.path(self)`*:
  Convert to a native filesystem `pathlib.Path`.

  *`FStr.posix_path(self)`*:
  Convert to a Posix filesystem `pathlib.Path`.

  *`FStr.windows_path(self)`*:
  Convert to a Windows filesystem `pathlib.Path`.



  Scan the string `s` for characters in `gochars` starting at `offset`.
  Return `(match,new_offset)`.

  `gochars` may also be a callable, in which case a character
  `ch` is accepted if `gochars(ch)` is true.



  Scan the string `s` for decimal characters starting at `offset` (default `0`).
  Return `(dec_string,new_offset)`.



  Fetch a decimal or basic float (nnn.nnn) value
  from the str `s` at `offset` (default `0`).
  Return `(value,new_offset)`.



  Scan the string `s` for a decimal value starting at `offset` (default `0`).
  Return `(value,new_offset)`.



  Collect text from the string `s` from position `offset` up
  to the first occurence of delimiter `delim`; return the text
  excluding the delimiter and the offset after the delimiter.



  Scan the string `s` for a dotted identifier (by default an
  ASCII letter or underscore followed by letters, digits or
  underscores) with optional trailing dot and another dotted
  identifier, starting at `offset` (default `0`).
  Return `(match,new_offset)`.

  Note: the empty string and an unchanged offset will be returned if
  there is no leading letter/underscore.

  Keyword arguments are passed to `get_identifier`
  (used for each component of the dotted identifier).



  Parse a simple environment variable reference to $varname or
  $x where "x" is a special character.

  Parameters:
  * `s`: the string with the variable reference
  * `offset`: the starting point for the reference
  * `default`: default value for missing environment variables;
     if `None` (the default) a `ValueError` is raised
  * `environ`: the environment mapping, default `os.environ`
  * `specials`: the mapping of special single character variables



  Scan the string `s` for hexadecimal characters starting at `offset` (default `0`).
  Return `(hex_string,new_offset)`.



  Scan the string `s` for a hexadecimal value starting at `offset` (default `0`).
  Return `(value,new_offset)`.



  Scan the string `s` for an identifier (by default an ASCII
  letter or underscore followed by letters, digits or underscores)
  starting at `offset` (default 0).
  Return `(match,new_offset)`.

  *Note*: the empty string and an unchanged offset will be returned if
  there is no leading letter/underscore.

  Parameters:
  * `s`: the string to scan
  * `offset`: the starting offset, default `0`.
  * `alpha`: the characters considered alphabetic,
    default `string.ascii_letters`.
  * `number`: the characters considered numeric,
    default `string.digits`.
  * `extras`: extra characters considered part of an identifier,
    default `'_'`.



  Parse a `[`*clausename*`]`*entryname* string
  from `s` at `offset` (default `0`).
  Return `(clausename,entryname,new_offset)`.



  Parse a `[`*clausename*`]` string from `s` at `offset` (default `0`).
  Return `(clausename,new_offset)`.



  Scan the string `s` for characters not in `string.whitespace`
  starting at `offset` (default `0`).
  Return `(match,new_offset)`.



  Scan the string `s` for characters not in `stopchars` starting
  at `offset` (default `0`).
  Return `(match,new_offset)`.



  Strip a leading `prefix` and numeric value `n` from the string `s`
  starting at `offset` (default `0`).
  Return the matched prefix, the numeric value and the new offset.
  Returns `(None,None,offset)` on no match.

  Parameters:
  * `s`: the string to parse
  * `prefix`: the prefix string which must appear at `offset`
    or an object with a `match(str,offset)` method
    such as an `re.Pattern` regexp instance
  * `n`: optional integer value;
    if omitted any value will be accepted, otherwise the numeric
    part must match `n`

  If `prefix` is a `str`, the "matched prefix" return value is `prefix`.
  Otherwise the "matched prefix" return value is the result of
  the `prefix.match(s,offset)` call. The result must also support
  a `.end()` method returning the offset in `s` beyond the match,
  used to locate the following numeric portion.

  Examples:

     >>> import re
     >>> get_prefix_n('s03e01--', 's')
     ('s', 3, 3)
     >>> get_prefix_n('s03e01--', 's', 3)
     ('s', 3, 3)
     >>> get_prefix_n('s03e01--', 's', 4)
     (None, None, 0)
     >>> get_prefix_n('s03e01--', re.compile('[es]',re.I))
     (<re.Match object; span=(0, 1), match='s'>, 3, 3)
     >>> get_prefix_n('s03e01--', re.compile('[es]',re.I), offset=3)
     (<re.Match object; span=(3, 4), match='e'>, 1, 6)



  Get quoted text with slosh escapes and optional environment substitution.

  Parameters:
  * `s`: the string containg the quoted text.
  * `offset`: the starting point, default `0`.
  * `q`: the quote character, default `'"'`. If `q` is `None`,
    do not expect the string to be delimited by quote marks.
  * `environ`: if not `None`, also parse and expand `$`*envvar* references.
  * `default`: passed to `get_envvar`



  Parse a double quoted string or an identifier.



  Collect slosh escaped text from the string `s` from position
  `offset` (default `0`) and return the decoded unicode string and
  the offset of the completed parse.

  Parameters:
  * `delim`: end of string delimiter, such as a single or double quote.
  * `offset`: starting offset within `s`, default `0`.
  * `slosh`: escape character, default a slosh ('\').
  * `mapper`: a mapping function which accepts a single character
    and returns a replacement string or `None`; this is used the
    replace things such as '\t' or '\n'. The default is the
    `slosh_mapper` function, whose default mapping is `SLOSH_CHARMAP`.
  * `specials`: a mapping of other special character sequences and parse
    functions for gathering them up. When one of the special
    character sequences is found in the string, the parse
    function is called to parse at that point.
    The parse functions accept
    `s` and the offset of the special character. They return
    the decoded string and the offset past the parse.

  The escape character `slosh` introduces an encoding of some
  replacement text whose value depends on the following character.
  If the following character is:
  * the escape character `slosh`, insert the escape character.
  * the string delimiter `delim`, insert the delimiter.
  * the character 'x', insert the character with code from the following
    2 hexadecimal digits.
  * the character 'u', insert the character with code from the following
    4 hexadecimal digits.
  * the character 'U', insert the character with code from the following
    8 hexadecimal digits.
  * a character from the keys of `mapper`



  Strip a trailing "part N" suffix from the string `s`.
  Return the matched suffix and the number part number.
  Retrn `(None,None)` on no match.

  Parameters:
  * `s`: the string
  * `keywords`: an iterable of `str` to match, or a single `str`;
    default `'part'`
  * `numeral_map`: an optional mapping of numeral names to numeric values;
    default `NUMERAL_NAMES['en']`, the English numerals

  Exanmple:

      >>> get_suffix_part('s09e10 - A New World: Part One')
      (': Part One', 1)



  Parse the string `s` from position `offset` using the supplied
  tokeniser functions `getters`.
  Return the list of tokens matched and the final offset.

  Parameters:
  * `s`: the string to parse.
  * `offset`: the starting position for the parse.
  * `getters`: an iterable of tokeniser specifications.

  Each tokeniser specification `getter` is either:
  * a callable expecting `(s,offset)` and returning `(token,new_offset)`
  * a literal string, to be matched exactly
  * a `tuple` or `list` with values `(func,args,kwargs)`;
    call `func(s,offset,*args,**kwargs)`
  * an object with a `.match` method such as a regex;
    call `getter.match(s,offset)` and return a match object with
    a `.end()` method returning the offset of the end of the match



  Scan the string `s` for an identifier as for `get_identifier`,
  but require the letters to be uppercase.



  Scan the string `s` for characters in `string.whitespace`
  starting at `offset` (default `0`).
  Return `(match,new_offset)`.



  Class decorator to walk this class for direct methods
  marked as for use in format strings
  and to include them in `cls.format_attributes()`.

  Methods are normally marked with the `@format_attribute` decorator.

  If `inherit` is true the base format attributes will be
  obtained from other classes:
  * `inherit` is `True`: use `cls.__mro__`
  * `inherit` is a class: use that class
  * otherwise assume `inherit` is an iterable of classes
  For each class `otherclass`, update the initial attribute
  mapping from `otherclass.get_format_attributes()`.



  A flavour of `binascii.hexlify` returning a `str`.



  Convert a string for safe transcription in HTML.

  Parameters:
  * `s`: the string
  * `nbsp`: replaces spaces with `"&nbsp;"` to prevent word folding,
    default `False`.



  Quote a string for use in HTML.



  Return the `paragraph` indented by `line_indent` (default `"  "`).



  Test if the string `s` is an identifier from position `offset` onward.



  Test if the string `s` is an identifier
  from position `offset` (default `0`) onward.



  Test if the string `s` is an uppercase identifier
  from position `offset` (default `0`) onward.



  Check that a string matches the regular expression `^[A-Z][A-Z_0-9]*$`.



  Quote a string for use in JavaScript.



  Return `value.lower()`
  with `'-'` translated into `'_'` and `' '` translated into `'-'`.

  I use this to construct lowercase filenames containing a
  readable transcription of a title string.

  See also `titleify_lc()`, an imperfect reversal of this.



  Wrapper for `get_tokens` which catches `ValueError` exceptions
  and returns `(None,offset)`.



  Take an attribute name `attr` and return `(key,is_plural)`.

  Examples:
  * `'FOO'` returns `('FOO',False)`.
  * `'FOOs'` or `'FOOes'` returns `('FOO',True)`.
  Otherwise return `(None,False)`.



  Quote a string for use in PHP code.



  Like `typed_str` but using `repr` instead of `str`.
  This is available as both `typed_repr` and `r`.



  Return "type(o).__name__:str(o)" for some object `o`.
  This is available as both `typed_str` and `s`.

  Parameters:
  * `use_cls`: default `False`;
    if true, use `str(type(o))` instead of `type(o).__name__`
  * `use_repr`: default `False`;
    if true, use `repr(o)` instead of `str(o)`

  I use this a lot when debugging. Example:

      from cs.lex import typed_str as s
      ......
      X("foo = %s", s(foo))



  Convenience routine for skipping past whitespace;
  returns the offset of the next nonwhitespace character.



  Return a string to replace backslash-`c`, or `None`.



  Convert a camel cased string `camelcased` into snake case.

  Parameters:
  * `cameelcased`: the cameel case string to convert
  * `first_letter_only`: optional flag (default `False`);
    if true then just ensure that the first character of a word
    is uppercased, otherwise use `str.title`

  Example:

      >>> snakecase('abcDef')
      'abc_def'
      >>> snakecase('abcDEf')
      'abc_def'
      >>> snakecase('AbcDef')
      'abc_def'



  Split a path with an optional leading `[user@]rhost:` prefix
  into the prefix and the remaining path.
  `None` is returned for the prefix is there is none.
  This is useful for things like `rsync` targets etc.



  Slightly smarter dedent which ignores a string's opening indent.

  Algorithm:
  strip the supplied string `s`, pull off the leading line,
  dedent the rest, put back the leading line.

  This supports my preferred docstring layout, where the opening
  line of text is on the same line as the opening quote.

  The optional `post_indent` parameter may be used to indent
  the dedented text before return.

  The optional `sub_indent` parameter may be used to indent
  the second and following lines if the dedented text before return.

  Examples:

      >>> def func(s):
      ...   """ Slightly smarter dedent which ignores a string's opening indent.
      ...       Strip the supplied string `s`. Pull off the leading line.
      ...       Dedent the rest. Put back the leading line.
      ...   """
      ...   pass
      ...
      >>> from cs.lex import stripped_dedent
      >>> print(stripped_dedent(func.__doc__))
      Slightly smarter dedent which ignores a string's opening indent.
      Strip the supplied string `s`. Pull off the leading line.
      Dedent the rest. Put back the leading line.
      >>> print(stripped_dedent(func.__doc__, sub_indent='  '))
      Slightly smarter dedent which ignores a string's opening indent.
        Strip the supplied string `s`. Pull off the leading line.
        Dedent the rest. Put back the leading line.
      >>> print(stripped_dedent(func.__doc__, post_indent='  '))
        Slightly smarter dedent which ignores a string's opening indent.
        Strip the supplied string `s`. Pull off the leading line.
        Dedent the rest. Put back the leading line.
      >>> print(stripped_dedent(func.__doc__, post_indent='  ', sub_indent='| '))
        Slightly smarter dedent which ignores a string's opening indent.
        | Strip the supplied string `s`. Pull off the leading line.
        | Dedent the rest. Put back the leading line.



  Convert an iterable to strings and join with `sep` (default `', '`).



  Compute some spaces to use a tab padding at an offfset.



  A generator yielding lines of values from `rows` aligned in columns.

  Each row in rows is a list of strings. If the strings contain
  newlines they will be split into subrows.

  Example:

      >>> for row in tabulate(
      ...     ['one col'],
      ...     ['three', 'column', 'row'],
      ...     ['row3', 'multi\nline\ntext', 'goes\nhere', 'and\nhere'],
      ...     ['two', 'cols'],
      ... ):
      ...     print(row)
      ...
      one col
      three    column  row
      row3     multi   goes  and
               line    here  here
               text
      two      cols
      >>>



  Transcribe the bytes `bs` to text using compact text runs for
  some common text values.

  This can be reversed with the `untexthexify` function.

  This is an ad doc format devised to be compact but also to
  expose "text" embedded within to the eye. The original use
  case was transcribing a binary directory entry format, where
  the filename parts would be somewhat visible in the transcription.

  The output is a string of hexadecimal digits for the encoded
  bytes except for runs of values from the whitelist, which are
  enclosed in the shiftin and shiftout markers and transcribed
  as is. The default whitelist is values of the ASCII letters,
  the decimal digits and the punctuation characters '_-+.,'.
  The default shiftin and shiftout markers are '[' and ']'.

  String objects converted with either `hexify` and `texthexify`
  output strings may be freely concatenated and decoded with
  `untexthexify`.

  Example:

      >>> texthexify(b'&^%&^%abcdefghi)(*)(*')
      '265e25265e25[abcdefghi]29282a29282a'

  Parameters:
  * `bs`: the bytes to transcribe
  * `shiftin`: Optional. The marker string used to indicate a shift to
    direct textual transcription of the bytes, default: `'['`.
  * `shiftout`: Optional. The marker string used to indicate a
    shift from text mode back into hexadecimal transcription,
    default `']'`.
  * `whitelist`: an optional bytes or string object indicating byte
    values which may be represented directly in text;
    the default value is the ASCII letters, the decimal digits
    and the punctuation characters `'_-+.,'`.



  Translate `'-'` into `' '` and `'_'` translated into `'-'`,
  then titlecased.

  See also `lc_()`, which this reverses imperfectly.



  Like `typed_str` but using `repr` instead of `str`.
  This is available as both `typed_repr` and `r`.



  Return "type(o).__name__:str(o)" for some object `o`.
  This is available as both `typed_str` and `s`.

  Parameters:
  * `use_cls`: default `False`;
    if true, use `str(type(o))` instead of `type(o).__name__`
  * `use_repr`: default `False`;
    if true, use `repr(o)` instead of `str(o)`

  I use this a lot when debugging. Example:

      from cs.lex import typed_str as s
      ......
      X("foo = %s", s(foo))



  Return the string `s` with `TAB`s expanded and control characters
  replaced with printable representations.



  Decode a textual representation of binary data into binary data.

  This is the reverse of the `texthexify` function.

  Outside of the `shiftin`/`shiftout` markers the binary data
  are represented as hexadecimal. Within the markers the bytes
  have the values of the ordinals of the characters.

  Example:

      >>> untexthexify('265e25265e25[abcdefghi]29282a29282a')
      b'&^%&^%abcdefghi)(*)(*'

  Parameters:
  * `s`: the string containing the text representation.
  * `shiftin`: Optional. The marker string commencing a sequence
    of direct text transcription, default `'['`.
  * `shiftout`: Optional. The marker string ending a sequence
    of direct text transcription, default `']'`.





  *Release 20241207*:
  tabulate: split cells containing newlines over multiple output rows.

  *Release 20241122*:
  tabulate: make the default separator two spaces instead of one, immediate return if no rows (avoids max() of empty list).

  *Release 20241119.1*:
  Add PyPI classifier, in part to test an updated release script.

  *Release 20241119*:
  stripped_dedent: new optional sub_indent parameter for indenting the second and following lines, handy for usage messages.

  *Release 20241109*:
  * stripped_dedent: new optional post_indent parameter to indent the dedented text.
  * New tabulate(*rows) generator function yielding lines of padded columns.

  *Release 20240630*:
  New indent(paragraph,line_indent="  ") function.

  *Release 20240519*:
  New get_suffix_part() to extract things line ": Part One" from something such as a TV episode name.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240211*:
  New split_remote_path() function to recognise [[user@]host]:path.

  *Release 20231018*:
  New is_uc_identifier function.

  *Release 20230401*:
  Import update.

  *Release 20230217.1*:
  Fix package requirements.

  *Release 20230217*:
  * New get_prefix_n function to parse a numeric value preceeded by a prefix.
  * Drop strip_prefix_n, get_prefix_n is more general and I had not got around to using strip_prefix_n yet - when I did, I ended up writing get_prefix_n.

  *Release 20230210*:
  * @has_format_attributes: new optional inherit parameter to inherit superclass (or other) format attributes, default False.
  * New FNumericMixin, FFloat, FInt FormatableMixin subclasses like FStr - they add .localtime and .utctime formattable attributes.

  *Release 20220918*:
  typed_str(): crop the value part, default max_length=32, bugfix message cropping.

  *Release 20220626*:
  * Remove dependency on cs.py3, we've been Python 2 incompatible for a while.
  * FormatableFormatter.format_field: promote None to FStr(None).

  *Release 20220227*:
  * typed_str,typed_repr: make max_length the first optional positional parameter, make other parameters keyword only.
  * New camelcase() and snakecase() functions.

  *Release 20211208*:
  Docstring updates.

  *Release 20210913*:
  * FormatableFormatter.FORMAT_RE_ARG_NAME_s: strings commencing with digits now match \d+(\.\d+)[a-z]+, eg "02d".
  * Alias typed_str as s and typed_repr as r.
  * FormatableFormatter: new .format_mode thread local state object initially with strict=False, used to control whether unknown fields leave a placeholder or raise KeyError.
  * FormatableFormatter.format_field: assorted fixes.

  *Release 20210906*:
  New strip_prefix_n() function to strip a leading `prefix` and numeric value `n` from the start of a string.

  *Release 20210717*:
  * Many many changes to FormatableMixin, FormatableFormatter and friends around supporting {foo|conv1|con2|...} instead of {foo!conv}. Still in flux.
  * New typed_repr like typed_str but using repr.

  *Release 20210306*:
  * New cropped() function to crop strings.
  * Rework cropped_repr() to do the repr() itself, and to crop the interiors of tuples and lists.
  * cropped_repr: new inner_max_length for cropping the members of collections.
  * cropped_repr: special case for length=1 tuples.
  * New typed_str(o) object returning type(o).__name__:str(o) in the default case, useful for debugging.

  *Release 20201228*:
  Minor doc updates.

  *Release 20200914*:
  * Hide terribly special purpose lastlinelen() in cs.hier under a private name.
  * New common_prefix and common_suffix function to compare strings.

  *Release 20200718*:
  get_chars: accept a callable for gochars, indicating a per character test function.

  *Release 20200613*:
  cropped_repr: replace hardwired 29 with computed length

  *Release 20200517*:
  * New get_ini_clausename to parse "[clausename]".
  * New get_ini_clause_entryname parsing "[clausename]entryname".
  * New cropped_repr for returning a shortened repr()+"..." if the length exceeds a threshold.
  * New format_escape function to double {} characters to survive str.format.

  *Release 20200318*:
  * New lc_() function to lowercase and dash a string, new titleify_lc() to mostly reverse lc_().
  * New format_as function, FormatableMixin and related FormatAsError.

  *Release 20200229*:
  New cutprefix and cutsuffix functions.

  *Release 20190812*:
  Fix bad slosh escapes in strings.

  *Release 20190220*:
  New function get_qstr_or_identifier.

  *Release 20181108*:
  new function get_decimal_or_float_value to read a decimal or basic float

  *Release 20180815*:
  No semantic changes; update some docstrings and clean some lint, fix a unit test.

  *Release 20180810*:
  * New get_decimal_value and get_hexadecimal_value functions.
  * New stripped_dedent function, a slightly smarter textwrap.dedent.

  *Release 20171231*:
  New function get_decimal. Drop unused function dict2js.

  *Release 20170904*:
  Python 2/3 ports, move rfc2047 into new cs.rfc2047 module.

  *Release 20160828*:
  * Use "install_requires" instead of "requires" in DISTINFO.
  * Discard str1(), pointless optimisation.
  * unrfc2047: map _ to SPACE, improve exception handling.
  * Add phpquote: quote a string for use in PHP code; add docstring to jsquote.
  * Add is_identifier test.
  * Add get_dotted_identifier.
  * Add is_dotted_identifier.
  * Add get_hexadecimal.
  * Add skipwhite, convenince wrapper for get_white returning just the next offset.
  * Assorted bugfixes and improvements.

  *Release 20150120*:
  cs.lex: texthexify: backport to python 2 using cs.py3 bytes type

  *Release 20150118*:
  metadata updates

  *Release 20150116*:
  PyPI metadata and slight code cleanup.


