# packages

pkgId: 1c261ffc85322b341680ea93b59e95b1408b99046000c6396d60688f5c2c5539
name: python-cs-mplutils
arch: src
version: 20241122
epoch: 0
release: 1
summary: A few conveniences for working with matplotlib.
url: https://pypi.org/project/cs-mplutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-mplutils-20241122-1.src.rpm
checksum_type: sha256
description:
  A few conveniences for working with matplotlib.

  *Latest release 20241122*:
  * FigureSize: fix field factories.
  * print_figure: wire a .png extension for the temp image.



  Return a set of `Axes`.

  Parameters:
  * `figure`: optional `Figure` from which to obtain the `Axes`
    or an `(x,y)` figure size or an `(x,y,dpi)` figure size
  * `ax`: optional `Axes` or axes index

  If `ax` is already an `Axes` it is returned unchanged.
  Otherwise `ax` should be the index of a set of axes,
  default `0`.

  If `figure` is a `Figure`, `ax` is used to select one of its
  sets of axes.

  Otherwise a `Figure` is created and a set of axes is selected.
  The default figure size comes from the `FigureSize` defaults.

  The `figure` positional parameter may be supplied
  as a 2-tuple `(fig_dx,fig_dy)` to override the default size
  or as a 3-tuple `(fig_dx,fig_dy,dpi)` to override the default size and dpi,
  or as a `FigureSze`.



  Specifications for a `Figure`'s dimensions.

  *`FigureSize.Figure(self, **kw)`*:
  Return a new `Figure` of this size.
  It will have no subplots.



  Print `figure_or_ax` to a file.

  Parameters:
  * `figure_or_ax`: a `matplotlib.figure.Figure` or an object
    with a `.figure` attribute such as a set of `Axes`
  * `imgformat`: optional output format; if omitted use `'sixel'`
    if `file` is a terminal, otherwise `'png'`
  * `file`: the output file, default `sys.stdout`



  Remove all decorations from a `Figure` or `Axes` instance,
  intended for making bare plots such as a tile in GUI.

  Presently this removes:
  - axes markings and legend from each axis
  - the padding from all the figure subplots



  Save a `Figure` to the file `imgpath`.

  Parameters:
  * `figure_or_ax`: a `matplotlib.figure.Figure` or an object
    with a `.figure` attribute such as a set of `Axes`
  * `imgpath`: the filesystem path to which to save the image
  * `force`: optional flag, default `False`: if true the `imgpath`
    will be written to even if it exists



  Context manager to save a `Figure` to a file and yield the file path.
  The file is removed on exit from the context.

  Parameters:
  * `figure_or_ax`: a `matplotlib.figure.Figure` or an object
    with a `.figure` attribute such as a set of `Axes`
  * `dir`: passed to `tempfile.TemporaryDirectory`
  * `ext`: optional file extension, default `'png'`





  *Release 20241122*:
  * FigureSize: fix field factories.
  * print_figure: wire a .png extension for the temp image.

  *Release 20230407*:
  FigureSize: get defaults via $FIGURE_SIZE_DX, $FIGURE_SIZE_DY and $FIGURE_SIZE_DPI envvars.

  *Release 20220918*:
  * New FigureSize class.
  * axes(): accept a FigureSize for figure.

  *Release 20220805*:
  Initial PyPI release.


pkgId: ff57c9c1cc4733d0a0bc53134a4cd8176425190b7259a0c8dd7aea6d49a09f1c
name: python-cs-pfx
arch: src
version: 20241208
epoch: 0
release: 1
summary: Easy context prefixes for messages.
url: https://pypi.org/project/cs-pfx/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-pfx-20241208-1.src.rpm
checksum_type: sha256
description:
  Dynamic message prefixes providing execution context.

  *Latest release 20241208*:
  Pfx.prefixify_exception: record the original message as {attr}_without_prefix, useful in warnings which themselves recite the prefix.

  The primary facility here is `Pfx`,
  a context manager which maintains a per thread stack of context prefixes.
  There are also decorators for functions.
  This stack is used to prefix logging messages and exception text with context.

  Usage is like this:

      from cs.pfx import Pfx
      ...
      def parser(filename):
        with Pfx(filename):
          with open(filename) as f:
            for lineno, line in enumerate(f, 1):
              with Pfx(lineno):
                if line_is_invalid(line):
                  raise ValueError("problem!")
                info("line = %r", line)

  This produces log messages like:

      datafile: 1: line = 'foo\n'

  and exception messages like:

      datafile: 17: problem!

  which lets one put just the relevant complaint in exception and log
  messages and get useful calling context on the output.
  This does make for wordier logs and exceptions
  but used with a little discretion produces far more debuggable results.



  A context manager to maintain a per-thread stack of message prefixes.

  *`Pfx.__init__(self, mark, *args, **kwargs)`*:
  Initialise a new Pfx instance.

  Parameters:
  * `mark`: message prefix string
  * `args`: if not empty, apply to the prefix string with `%`
  * `absolute`: optional keyword argument, default `False`. If
    true, this message forms the base of the message prefixes;
    earlier prefixes will be suppressed.
  * `loggers`: which loggers should receive log messages.
  * `print`: if true, print the `mark` on entry to the `with` suite.
    This may be a `bool`, implying `print()` if `True`,
    a callable which works like `print()`,
    or a file-like object which implies using `print(...,file=print)`.

  *Note*:
  the `mark` and `args` are only combined if the `Pfx` instance gets used,
  for example for logging or to annotate an exception.
  Otherwise, they are not combined.
  Therefore the values interpolated are as they are when the `Pfx` is used,
  not necessarily as they were when the `Pfx` was created.
  If the `args` are subject to change and you require the original values,
  apply them to `mark` immediately, for example:

      with Pfx('message %s ...' % (arg1, arg2, ...)):

  This is a bit more expensive as it incurs the formatting cost
  whenever you enter the `with` clause.
  The common usage is:

      with Pfx('message %s ...', arg1, arg2, ...):

  *`Pfx.critical(self, msg, *args, **kwargs)`*:
  Emit a critical log message.

  *`Pfx.debug(self, msg, *args, **kwargs)`*:
  Emit a debug log message.

  *`Pfx.error(self, msg, *args, **kwargs)`*:
  Emit an error log message.

  *`Pfx.exception(self, msg, *args, **kwargs)`*:
  Log an exception message to this Pfx's loggers.

  *`Pfx.info(self, msg, *args, **kwargs)`*:
  Emit an info log message.

  *`Pfx.log(self, level, msg, *args, **kwargs)`*:
  Log a message at an arbitrary log level to this Pfx's loggers.

  *`Pfx.loggers`*:
  Return the loggers to use for this Pfx instance.

  *`Pfx.logto(self, new_loggers)`*:
  Define the Loggers anew.

  *`Pfx.partial(self, func, *a, **kw)`*:
  Return a function that will run the supplied function `func`
  within a surrounding `Pfx` context with the current mark string.

  This is intended for deferred call facilities like
  `Later` and `futures`.

  *`Pfx.prefixify(text)`*:
  Return `text` with the current prefix prepended.
  Return `text` unchanged if it is not a string.

  *`Pfx.prefixify_exception(e)`*:
  Modify the supplied exception `e` with the current prefix.
  Return `True` if modified, `False` if unable to modify.

  *`Pfx.push(msg, *a)`*:
  A new `Pfx(msg,*a)` onto the `Thread` stack.

  *`Pfx.scope(msg=None, *a)`*:
  Context manager to save the current `Thread`'s stack state
  and to restore it on exit.

  This is to aid long suites which progressively add `Pfx` context
  as the suite progresses, example:

      for item in items:
          with Pfx.scope("item %s", item):
              db_row = db.get(item)
              Pfx.push("db_row = %r", db_row)
              matches = db.lookup(db_row.category)
              if not matches:
                  continue
              Pfx.push("%d matches", len(matches):
              ... etc etc ...

  *`Pfx.umark`*:
  Return the unicode message mark for use with this Pfx.

  This is used by Pfx._state.prefix to compute the full prefix.

  *`Pfx.warning(self, msg, *args, **kwargs)`*:
  Emit a warning log message.



  General purpose @pfx for generators, methods etc.

  Parameters:
  * `func`: the function or generator function to decorate
  * `message`: optional prefix to use instead of the function name
  * `message_args`: optional arguments to embed in the preifx using `%`

  Example usage:

      @pfx
      def f(....):
          ....



  Call `func(*a,**kw)` within an enclosing `Pfx` context manager
  reciting the function name and arguments.

  Example:

      >>> import os
      >>> pfx_call(os.rename, "oldname", "newname")



  Wrapper for iterables to prefix exceptions with `tag`.



  Decorator to provide a `Pfx` context for an instance method prefixing
  *classname.methodname*.

  If `use_str` is true (default `False`)
  use `str(self)` instead of `classname`.

  If `with_args` is true (default `False`)
  include the specified arguments in the `Pfx` context.
  If `with_args` is `True`, this includes all the arguments.
  Otherwise `with_args` should be a sequence of argument references:
  an `int` specifies one of the positional arguments
  and a string specifies one of the keyword arguments.

  Examples:

      class O:
          
          @pfx_method
          def foo(self, .....):
              ....
          
          @pfx_method(use_str=True)
          def foo2(self, .....):
              ....
          
          @pfx_method(with_args=True)
          def foo3(self, a, b, c, *, x=1, y):
              ....
          
          @pfx_method(with_args=[1,2,'x'])
          def foo3(self, a, b, c, *, x=1, y):
              ....



  Subclass of Pfx to insert current function and caller into messages.



  Call `print()` with the current prefix.

  The optional keyword parameter `print_func`
  provides an alternative function to the builtin `print()`.



  Factory function returning a `Thread`
  which presents the current prefix as context.



  Return the current Pfx prefix.



  Push a temporary value for Pfx._state._ur_prefix to enloundenify messages.



  Strip the leading prefix from the string `s`
  using the prefix delimiter `sep`
  (default from `DEFAULT_SEPARATOR`: `': '`).

  This is a simple hack to support reporting error messages
  which have had a prefix applied,
  and fails accordingly if the base message itself contains the separator.



  Variation on `cs.x.X`
  which prefixes the message with the current Pfx prefix.



  Trite wrapper for `XP()` to transiently insert a leading prefix string.

  Example:

      XX("NOTE!", "some message")





  *Release 20241208*:
  Pfx.prefixify_exception: record the original message as {attr}_without_prefix, useful in warnings which themselves recite the prefix.

  *Release 20240630*:
  @pfx_method: fix access to class name when the target is a class.

  *Release 20240412*:
  Pfx.prefixify_exception: bugfix prefixification of sequences.

  *Release 20240326*:
  * Pfx.prefixify_exception: sanity check the types of the prefixed attributes, aids debugging.
  * Pfx.prefixify_exception: do not modify .message if it is not a string.

  *Release 20230604*:
  @pfx_method: handle methods with no __name__ (generally a misuse of the decorator).

  *Release 20230331*:
  PfxThread: use HasThreadState.Thread, make target mandatory.

  *Release 20221118*:
  pkg_tags: cs.py.func: update PyPI release: set pypi.release='20221118' [IGNORE]

  *Release 20220918*:
  * Drop _PfxThreadState.raise_needs_prefix, supplant with more reliable special exception attribute.
  * Pfx.__exit__: include more detail in (suppressed) "message not prefixed" message.
  * Pfx.__exit__: more elaborate logic for exc_value.args.

  *Release 20220523*:
  Pfx.umask: promote self.mark directly to ustr.

  *Release 20220429*:
  * New Pfx.scope() context manager and Pfx.push(msg,*a) nonindenting Pfx push.
  * pfxprint: new optional print_func keyword parameter.

  *Release 20220227*:
  * Pfx.prefixify: change OSError.args action: prefixify the first string.
  * XP: use DEFAULT_SEPARATOR on both paths.

  *Release 20211031*:
  Pfx.prefixify_exception: skip attributes which are None.

  *Release 20210913*:
  Pfx: do not fiddle with LookupError.args[0], it is the key.

  *Release 20210906*:
  * New pfxprint which calls print() with the current prefix.
  * Pfx.prefixify_exception: catch unexpected OSError.args value and report.
  * @pfx: use pfx_call if there is no presupplied message argument.

  *Release 20210801*:
  Bugfix for @pfx.

  *Release 20210731*:
  * Pfx.__exit__: special handling for some exception types.
  * New pfx_call(func,*a,**kw) function to concisely wrap single function calls.

  *Release 20210717*:
  @pfx_method: new optional decorator argument "with_args" to include some or all arguments in the Pfx context.

  *Release 20201227*:
  * Pfx: new print=False option to issue a print() or other call on entry to the with-suite eg with Pfx(....,print=verbose).
  * Pfx: print= now also accepts a file-like object.

  *Release 20201105*:
  @pfx: bugfix for generator functions.

  *Release 20201025*:
  * Refactor @pfx using @cs.deco.contextdecorator.
  * New unpfx() function, a crude hack to strip an applpied cs.pfx prefix from a string.
  * XP: just shim cs.x.X as callers expect, toss dubious extra functionality.
  * exception(): plumb keyword arguments.

  *Release 20200517*:
  * @pfx: handle normal functions and also generators, improve behaviour with the wrapped docstring.
  * @pfx_method: @pfx for methods.
  * @pfxtag obsoleted by new @pfx.

  *Release 20191004*:
  @pfx_method: new optional `use_str` parameter to use str(self) instead of type(self).__name__; now requires @cs.deco.decorator

  *Release 20190905*:
  * Pfx.__exit__: simplify prefixify_exc() logic, prefixify all suitable attributes.
  * New @pfx_method decorator for instance methods.

  *Release 20190607*:
  Pfx.__exit__ improved exception attribute handling.

  *Release 20190403*:
  Debugging aid: Pfx.umark: emit stack traceback on format conversion error.

  *Release 20190327*:
  * @pfx: set __name__ on the wrapper function.
  * Bugfix some references to the internal prefixify function.

  *Release 20190324*:
  Pfx.__exit__: apply the prefix to all the standard attributes where present, improves some message behaviour for some exception types.

  *Release 20181231*:
  Bugfix for an infinite regress.

  *Release 20181109*:
  * Update @contextmanager formalism to use try/finally for the cleanup phase.
  * New decorator @gen to manage Pfx state across generator iterations; pretty clunky.
  * Better fallback handling.
  * Some docstring updates.

  *Release 20170910*:
  Slight linting.

  *Release 20170903.1*:
  corrections to the module docstring

  *Release 20170903*:
  Initial release for PyPI.


pkgId: 88e366155314f08376b7459c76e632861091138b310270d392032815c1b8c6b6
name: python-cs-progress
arch: src
version: 20241122
epoch: 0
release: 1
url: https://pypi.org/project/cs-progress/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-progress-20241122-1.src.rpm
checksum_type: sha256
summary:
  A progress tracker with methods for throughput, ETA and update notification; also a compound progress meter composed from other progress meters.

description:
  A progress tracker with methods for throughput, ETA and update notification;
  also a compound progress meter composed from other progress meters.

  *Latest release 20241122*:
  * BaseProgress.bar: default for report_print now comes from not BaseCommand.Options.quiet.
  * BaseProgress.bar: always print the final report if report_print.



  Decorator for a function accepting an optional `progress`
  keyword parameter.
  If `progress` is `None` and the default `Upd` is not disabled,
  run the function with a progress bar.



  The base class for `Progress` and `OverProcess`
  with various common methods.

  Note that durations are in seconds
  and that absolute time is in seconds since the UNIX epoch
  (the basis of `time.time()`).

  *`BaseProgress.__init__(self, name=None, start_time=None, units_scale=None)`*:
  Initialise a progress instance.

  Parameters:
  * `name`: optional name
  * `start_time`: optional UNIX epoch start time, default from `time.time()`
  * `units_scale`: a scale for use with `cs.units.transcribe`,
    default `BINARY_BYTES_SCALE`

  *`BaseProgress.__eq__(self, other)`*:
  A Progress is equal to another object `other`
  if its position equals `int(other)`.

  *`BaseProgress.__ge__(self, other, NotImplemented=NotImplemented)`*:
  Return a >= b.  Computed by @total_ordering from (not a < b).

  *`BaseProgress.__gt__(self, other, NotImplemented=NotImplemented)`*:
  Return a > b.  Computed by @total_ordering from (not a < b) and (a != b).

  *`BaseProgress.__int__(self)`*:
  `int(Progress)` returns the current position.

  *`BaseProgress.__le__(self, other, NotImplemented=NotImplemented)`*:
  Return a <= b.  Computed by @total_ordering from (a < b) or (a == b).

  *`BaseProgress.__lt__(self, other)`*:
  A Progress is less then another object `other`
  if its position is less than `int(other)`.

  *`BaseProgress.arrow(self, width, no_padding=False)`*:
  Construct a progress arrow representing completion
  to fit in the specified `width`.

  *`BaseProgress.bar(*a, upd: Optional[cs.upd.Upd] = <function uses_upd.<locals>.<lambda> at 0x10a711cf0>, **kw)`*:
  A context manager to create and withdraw a progress bar.
  It returns the `UpdProxy` which displays the progress bar.

  Parameters:
  * `label`: a label for the progress bar,
    default from `self.name`.
  * `statusfunc`: an optional function to compute the progress bar text
    accepting `(self,label,width)`.
  * `width`: an optional width expressing how wide the progress bar
    text may be.
    The default comes from the `proxy.width` property.
  * `recent_window`: optional timeframe to define "recent" in seconds;
    if the default `statusfunc` (`Progress.status`) is used
    this is passed to it
  * `report_print`: optional `print` compatible function
    with which to write a report on completion;
    this may also be a `bool`, which if true will use `Upd.print`
    in order to interoperate with `Upd`.
  * `stalled`: optional string to replace the word `'stalled'`
    in the status line; for a worked this might be betteer as `'idle'`
  * `insert_pos`: where to insert the progress bar, default `1`
  * `poll`: an optional callable accepting a `BaseProgress`
    which can be used to update the progress state before
    updating the progress bar display

  Example use:

      
      
      
      P = Progress(name=label)
      with P.bar(report_print=True):
          upload_filename(src, progress=P)

  *`BaseProgress.elapsed_time`*:
  Time elapsed since `start_time`.

  *`BaseProgress.eta`*:
  The projected time of completion: now + `remaining_time`.

  If `remaining_time` is `None`, this is also `None`.

  *`BaseProgress.format_counter(self, value, scale=None, max_parts=2, sep=',', **kw)`*:
  Format `value` accoridng to `scale` and `max_parts`
  using `cs.units.transcribe`.

  *`BaseProgress.iterbar(self, it, label=None, *, itemlenfunc=None, incfirst=False, update_period=0.3, **bar_kw)`*:
  An iterable progress bar: a generator yielding values
  from the iterable `it` while updating a progress bar.

  Parameters:
  * `it`: the iterable to consume and yield.
  * `label`: a label for the progress bar,
    default from `self.name`.
  * `itemlenfunc`: an optional function returning the "size" of each item
    from `it`, used to advance `self.position`.
    The default is to assume a size of `1`.
    A convenient alternative choice may be the builtin function `len`.
  * `incfirst`: whether to advance `self.position` before we
    `yield` an item from `it` or afterwards.
    This reflects whether it is considered that progress is
    made as items are obtained or only after items are processed
    by whatever is consuming this generator.
    The default is `False`, advancing after processing.
  * `update_period`: default `DEFAULT_UPDATE_PERIOD`; if `0`
    then update on every iteration, otherwise every `update_period`
    seconds
  Other parameters are passed to `Progress.bar`.

  Example use:

      from cs.units import DECIMAL_SCALE
      rows = [some list of data]
      P = Progress(total=len(rows), units_scale=DECIMAL_SCALE)
      for row in P.iterbar(rows, incfirst=True):
          ... do something with each row ...

      f = open(data_filename, 'rb')
      datalen = os.stat(f).st_size
      def readfrom(f):
          while True:
              bs = f.read(65536)
              if not bs:
                  break
              yield bs
      P = Progress(total=datalen)
      for bs in P.iterbar(readfrom(f), itemlenfunc=len):
          ... process the file data in bs ...

  *`BaseProgress.ratio`*:
  The fraction of progress completed: `(position-start)/(total-start)`.
  Returns `None` if `total` is `None` or `total<=start`.

  Example:

      >>> P = Progress()
       P.ratio
      >>> P.total = 16
      >>> P.ratio
      0.0
      >>> P.update(4)
      >>> P.ratio
      0.25

  *`BaseProgress.remaining_time`*:
  The projected time remaining to end
  based on the `throughput` and `total`.

  If `total` is `None`, this is `None`.

  *`BaseProgress.status(self, label, width, recent_window=None, stalled=None)`*:
  A progress string of the form:
  *label*`: `*pos*`/`*total*` ==>  ETA '*time*

  Parameters:
  * `label`: the label for the status line;
    if `None` use `self.name`
  * `width`: the available width for the status line;
    if not an `int` use `width.width`
  * `recent_window`: optional timeframe to define "recent" in seconds,
    default : `5`
  * `stalled`: the label to indicate no throughput, default `'stalled'`;
    for a worker this might often b better as `'idle'`

  *`BaseProgress.text_pos_of_total(self, fmt=None, fmt_pos=None, fmt_total=None, pos_first=False)`*:
  Return a "total:position" or "position/total" style progress string.

  Parameters:
  * `fmt`: format string interpolating `pos_text` and `total_text`.
    Default: `"{pos_text}/{total_text}"` if `pos_first`,
    otherwise `"{total_text}:{pos_text}"`
  * `fmt_pos`: formatting function for `self.position`,
    default `self.format_counter`
  * `fmt_total`: formatting function for `self.total`,
    default from `fmt_pos`
  * `pos_first`: put the position first if true (default `False`),
    only consulted if `fmt` is `None`

  *`BaseProgress.throughput`*:
  The overall throughput: `self.throughput_overall()`.

  By comparison,
  the `Progress.throughput` property is `self.throughput_recent`
  if the `throughput_window` is not `None`,
  otherwise it falls back to `throughput_overall`.

  *`BaseProgress.throughput_overall(self)`*:
  The overall throughput from `start` to `position`
  during `elapsed_time`.

  *`BaseProgress.throughput_recent(self, time_window)`*:
  The recent throughput. Implemented by subclasses.



  CheckPoint(time, position)

  *`CheckPoint.position`*:
  Alias for field number 1

  *`CheckPoint.time`*:
  Alias for field number 0



  A `Progress`-like class computed from a set of subsidiary `Progress`es.

  AN OverProgress instance has an attribute ``notify_update`` which
  is a set of callables.
  Whenever the position of a subsidiary `Progress` is updated,
  each of these will be called with the `Progress` instance and `None`.

  Example:

      >>> P = OverProgress(name="over")
      >>> P1 = Progress(name="progress1", position=12)
      >>> P1.total = 100
      >>> P1.advance(7)
      >>> P2 = Progress(name="progress2", position=20)
      >>> P2.total = 50
      >>> P2.advance(9)
      >>> P.add(P1)
      >>> P.add(P2)
      >>> P1.total
      100
      >>> P2.total
      50
      >>> P.total
      150
      >>> P1.start
      12
      >>> P2.start
      20
      >>> P.start
      0
      >>> P1.position
      19
      >>> P2.position
      29
      >>> P.position
      16

  *`OverProgress.add(self, subprogress)`*:
  Add a subsidairy `Progress` to the contributing set.

  *`OverProgress.eta`*:
  The `eta` is the maximum of the subsidiary etas.

  *`OverProgress.position`*:
  The `position` is the sum off the subsidiary position offsets
  from their respective starts.

  *`OverProgress.remove(self, subprogress, accrue=False)`*:
  Remove a subsidairy `Progress` from the contributing set.

  *`OverProgress.start`*:
  We always return a starting value of 0.

  *`OverProgress.throughput`*:
  The `throughput` is the sum of the subsidiary throughputs.

  *`OverProgress.throughput_recent(self, time_window)`*:
  The `throughput_recent` is the sum of the subsidiary throughput_recentss.

  *`OverProgress.total`*:
  The `total` is the sum of the subsidiary totals.



  A progress counter to track task completion with various utility methods.

  Example:

      >>> P = Progress(name="example")
      >>> P                         #doctest: +ELLIPSIS
      Progress(name='example',start=0,position=0,start_time=...,throughput_window=None,total=None):[CheckPoint(time=..., position=0)]
      >>> P.advance(5)
      >>> P                         #doctest: +ELLIPSIS
      Progress(name='example',start=0,position=5,start_time=...,throughput_window=None,total=None):[CheckPoint(time=..., position=0), CheckPoint(time=..., position=5)]
      >>> P.total = 100
      >>> P                         #doctest: +ELLIPSIS
      Progress(name='example',start=0,position=5,start_time=...,throughput_window=None,total=100):[CheckPoint(time=..., position=0), CheckPoint(time=..., position=5)]

  A Progress instance has an attribute ``notify_update`` which
  is a set of callables. Whenever the position is updated, each
  of these will be called with the `Progress` instance and the
  latest `CheckPoint`.

  `Progress` objects also make a small pretense of being an integer.
  The expression `int(progress)` returns the current position,
  and `+=` and `-=` adjust the position.

  This is convenient for coding, but importantly it is also
  useful for discretionary use of a Progress with some other
  object.
  If you want to make a lightweight `Progress` capable class
  you can set a position attribute to an `int`
  and manipulate it carefully using `+=` and `-=` entirely.
  If you decide to incur the cost of maintaining a `Progress` object
  you can slot it in:

      
      my_thing.amount = 0

      
      my_thing.amount = Progress(my_thing.amount)

  *`Progress.__init__(self, name: Optional[str] = None, *, position: Optional[float] = None, start: Optional[float] = None, start_time: Optional[float] = None, throughput_window: Optional[int] = None, total: Optional[float] = None, units_scale=None)`*:
  Initialise the Progesss object.

  Parameters:
  * `position`: initial position, default `0`.
  * `name`: optional name for this instance.
  * `start`: starting position of progress range,
    default from `position`.
  * `start_time`: start time of the process, default now.
  * `throughput_window`: length of throughput time window in seconds,
    default None.
  * `total`: expected completion value, default None.

  *`Progress.__iadd__(self, delta)`*:
  Operator += form of advance().

  >>> P = Progress()
  >>> P.position
  0
  >>> P += 4
  >>> P.position
  4
  >>> P += 4
  >>> P.position
  8

  *`Progress.__isub__(self, delta)`*:
  Operator -= form of advance().

  >>> P = Progress()
  >>> P.position
  0
  >>> P += 4
  >>> P.position
  4
  >>> P -= 4
  >>> P.position
  0

  *`Progress.advance(self, delta, update_time=None)`*:
  Record more progress, return the advanced position.

  >>> P = Progress()
  >>> P.position
  0
  >>> P.advance(4)
  >>> P.position
  4
  >>> P.advance(4)
  >>> P.position
  8

  *`Progress.advance_total(self, delta)`*:
  Function form of addition to the total.

  *`Progress.latest`*:
  Latest datum.

  *`Progress.position`*:
  Latest position.

  *`Progress.throughput`*:
  Current throughput per second.

  If `self.throughput_window` is not `None`,
  calls `self.throughput_recent(throughput_window)`.
  Otherwise call `self.throughput_overall()`.

  *`Progress.throughput_recent(self, time_window)`*:
  Recent throughput per second within a time window in seconds.

  The time span overlapping the start of the window is included
  on a flat pro rata basis.

  *`Progress.total`*:
  Return the current total.

  *`Progress.update(self, new_position, update_time=None)`*:
  Record more progress.

  >>> P = Progress()
  >>> P.position
  0
  >>> P.update(12)
  >>> P.position
  12



  Convenience function to construct and run a `Progress.iterbar`
  wrapping the iterable `it`,
  issuing and withdrawning a progress bar during the iteration.

  Parameters:
  * `it`: the iterable to consume
  * `label`: optional label, doubles as the `Progress.name`
  * `position`: optional starting position
  * `total`: optional value for `Progress.total`,
    default from `len(it)` if supported.
  * `units_scale`: optional units scale for `Progress`,
    default `UNSCALED_SCALE`

  If `total` is `None` and `it` supports `len()`
  then the `Progress.total` is set from it.

  All arguments are passed through to `Progress.iterbar`.

  Example use:

      for row in progressbar(rows):
          ... do something with row ...



  Exercise some of the functionality.





  *Release 20241122*:
  * BaseProgress.bar: default for report_print now comes from not BaseCommand.Options.quiet.
  * BaseProgress.bar: always print the final report if report_print.

  *Release 20240412*:
  * BaseProgress.status: fixes for the arrow_width computation.
  * BaseProgress.bar: drop existing UpdProxy support, drop deferred, implement update_period using a ticker Thread.
  * BaseProgress.bar: set update_period=DEFAULT_UPDATE_PERIOD by default.
  * Progress.iterbar: drop preexisting UpdProxy support, update_frequency and update_min_size support.
  * Progress: new advance_total(delta) method so that we have a callable for this.
  * BaseProgress.bar: new optional poll parameter accepting a callable accepting a BaseProgress to update the state before updating the bar display.
  * BaseProgress.bar: new stalled='stalled' parameter to specify the term for no recent throughput, workers might prefer 'idle'.
  * progressbar() updated to match.

  *Release 20230401*:
  progressbar, BaseProgress.iterbar: use @uses_upd to provide a context Upd instance.

  *Release 20230212*:
  BaseProgress: new update_period=0.2 parameter to constraint updates by elapsed time.

  *Release 20221207*:
  * BaseProgress.format_counter: accept arbitrary keyword arguments to pass to cs.units.transcribe.
  * Progress.__init__: accept floats instead of just ints.

  *Release 20220918*:
  Progress.iterbar: wrap the iteration in a try/finally for cleanup.

  *Release 20211208*:
  * Progress.__init__: make the first optional positional parameter be "name", make other parameters keyword only.
  * Progress.bar: make "label" the first optional positional parameter, make others keyword only.

  *Release 20210803*:
  * progressbar,iterbar: accept optional RunState to cancel iteration.
  * BaseProgress.iterbar: make update_min_size properly optional, was making update_frequency ineffective.

  *Release 20210730*:
  When there is no total just report position and no ETA.

  *Release 20210717*:
  Minor tweaks.

  *Release 20210316*:
  * Progress.iterbar: only update the status line once per iteration, either before or after the yield according to incfirst.
  * Progress.iterbar: fix the meaning of update_frequency to count iterations, add update_min_size to count progress advance.

  *Release 20210306*:
  progressbar: accept new optional `position` parameter, used to initialise the Progress.

  *Release 20201102.1*:
  DISTINFO: fix module dependencies.

  *Release 20201102*:
  * Format/layout changes for the default status line.
  * Progress.throughtput_recent: return None if no new positions beyond the starting position.
  * BaseProgress.status: accept label=None (default to self.name) and width=UpdProxy (uses width.width).
  * BaseProgress.status: new optional window parameter, default 5, defining the recent throughput window size in seconds.
  * A few bugfixes.

  *Release 20201025*:
  * Some formatting improvements.
  * BaseProgress.bar: new insert_pos parameter to position the progress bar, default still 1.
  * BaseProgress.bar: new deferred parameter putting off the status bar until the first update.
  * BaseProgress.bar: accept new optional `proxy` parameter to use (and not delete) an existing UpdProxy for display.
  * Progress.text_pos_of_total: new `pos_first=False` parameter, rendering the total before the position by default (less progress bar noise).
  * New @auto_progressbar decorator to provide a progress bar and initialise progress= parameter to functions which can use a Progress for reporting.
  * Assorted fixes.

  *Release 20200718.3*:
  BaseProgress.bar, progressbar: new optional report_print parameter for reporting on completion.

  *Release 20200718.2*:
  Bugfix: BaseProgress.status: handle throughput=0 when total=None.

  *Release 20200718.1*:
  BaseProgress.bar, progressbar: new optional update_frequency parameter for less frequent updates.

  *Release 20200718*:
  * Readability improvement for default status line.
  * progressbar: default units_scale=UNSCALED_SCALE.

  *Release 20200716.1*:
  BaseProgress.status: round throughput to an int if >=10.

  *Release 20200716*:
  * BaseProgress.status: distinguish "idle" (position >= total) from "stalled" (position < total).
  * BaseProgress.status: make the status very short if the progress is idle.

  *Release 20200627*:
  * BaseProgress.status: handle throughput=None (before any activity).
  * BaseProgress: drop count_of_total_bytes_text, superceded by format_counter (which honours the units_scale).

  *Release 20200626*:
  * New Progress.bar generator method iterating over an iterable while displaying a progress bar.
  * New convenience function progressbar(it,...) which rolls its own Progress instance.
  * Progress: always support a throughput window, default to DEFAULT_THROUGHPUT_WINDOW = 5s.
  * Improve the default progress bar render returned by Progress.status().

  *Release 20200613*:
  * BaseProgress, Progress and OverProgress now accept an optional units_scale, such as cs.units.UNSCALED_SCALE, to use when expressing progress - the default remains BINARY_SCALE.
  * New arrow(), format_counter() and text_pos_of_total() methods to produce components of the status string for tuning or external reuse.

  *Release 20200520*:
  OverProgress: throughput and eta implementations.

  *Release 20200129.3*:
  Test __version__ machinery again.

  *Release 20200129.2*:
  set __version__ to '20200129.2'

  *Release 20200129.1*:
  Dummy release to test new __version__.

  *Release 20200129*:
  New Progress.count_of_total_bytes_text property presenting "3kB/40MB" style text.

  *Release 20190812*:
  * New OverProgress class which is a composite of a set of subsidiary Progress instances.
  * Assorted other small updates.

  *Release 20190220*:
  * Progress: be somewhat like an int.
  * New status() method returning a convenient one line progress status report.

  *Release 20180703.2*:
  Progress: make .total into a property in order to fire the update notifications.

  *Release 20180703.1*:
  Progress: additions and changes to API: new .ratio, .elapsed_time, rename .projected to .remaining_time.

  *Release 20180703*:
  Initial release of cs.progress.


pkgId: 51e067ab479b1b52f9ed4f070e6a4ca7218631bf80718e6b99dbed2e6e10f8f1
name: python-cs-psutils
arch: src
version: 20241122
epoch: 0
release: 1
summary: Assorted process and subprocess management functions.
url: https://pypi.org/project/cs-psutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-psutils-20241122-1.src.rpm
checksum_type: sha256
description:
  Assorted process and subprocess management functions.

  *Latest release 20241122*:
  * print_argv: new print= parameter to provide a print() function, refactor to use print instead of file.write.
  * run: new optional print parameter, plumb to print_argv.
  * Use @uses_doit and @uses_quiet to provide the default quiet and doit states.

  Not to be confused with the excellent
  (psutil)[https://pypi.org/project/psutil/] package.



  Distribute the array `argv` over multiple arrays
  to fit within `MAX_ARGV`.
  Return a list of argv lists.

  Parameters:
  * `pre_argv`: the sequence of leading arguments
  * `argv`: the sequence of arguments to distribute; this may not be empty
  * `post_argv`: optional, the sequence of trailing arguments
  * `max_argv`: optional, the maximum length of each distributed
    argument list, default from `MAX_ARGV`: `131072`
  * `encode`: default `False`.
    If true, encode the argv sequences into bytes for accurate tallying.
    If `encode` is a Boolean,
    encode the elements with their .encode() method.
    If `encode` is a `str`, encode the elements with their `.encode()`
    method with `encode` as the encoding name;
    otherwise presume that `encode` is a callable
    for encoding each element.

  The returned argv arrays will contain the encoded element values.



  Context manager for a pid file.

  Parameters:
  * `path`: the path to the process id file.
  * `pid`: the process id to store in the pid file,
    default from `os.etpid`.

  Writes the process id file at the start
  and removes the process id file at the end.



  Pipe text (usually) from a command using `subprocess.Popen`.
  Return the `Popen` object with `.stdout` as a pipe.

  Parameters:
  * `argv`: the command argument list
  * `quiet`: optional flag, default `False`;
    if true, print the command to `stderr`
  * `text`: optional flag, default `True`; passed to `Popen`.
  * `stdin`: optional value for `Popen`'s `stdin`, default `DEVNULL`
  Other keyword arguments are passed to `Popen`.

  Note that `argv` is passed through `prep_argv` before use,
  allowing direct invocation with conditional parts.
  See the `prep_argv` function for details.



  Pipe text to a command.
  Optionally trace invocation.
  Return the Popen object with .stdin encoded as text.

  Parameters:
  * `argv`: the command argument list
  * `trace`: if true (default `False`),
    if `trace` is `True`, recite invocation to stderr
    otherwise presume that `trace` is a stream
    to which to recite the invocation.

  Other keyword arguments are passed to the `io.TextIOWrapper`
  which wraps the command's input.

  Note that `argv` is passed through `prep_argv` before use,
  allowing direct invocation with conditional parts.
  See the `prep_argv` function for details.



  A trite list comprehension to reduce an argument list `*argv`
  to the entries which are not `None` or `False`
  and to flatten other entries which are not strings.

  This exists ease the construction of argument lists
  with methods like this:

      >>> command_exe = 'hashindex'
      >>> hashname = 'sha1'
      >>> quiet = False
      >>> verbose = True
      >>> prep_argv(
      ...     command_exe,
      ...     quiet and '-q',
      ...     verbose and '-v',
      ...     hashname and ('-h', hashname),
      ... )
      ['hashindex', '-v', '-h', 'sha1']

  where `verbose` is a `bool` governing the `-v` option
  and `hashname` is either `str` to be passed with `-h hashname`
  or `None` to omit the option.



  Print an indented possibly folded command line.



  Truncate and remove a pidfile, permissions permitting.



  Run a command via `subprocess.run`.
  Return the `CompletedProcess` result or `None` if `doit` is false.

  Parameters:
  * `argv`: the command line to run
  * `doit`: optional flag, default `True`;
    if false do not run the command and return `None`
  * `logger`: optional logger, default `None`;
    if `True`, use `logging.getLogger()`;
    if not `None` or `False` trace using `print_argv`
  * `quiet`: default `True`; if false, print the command and its output
  * `input`: default `None`: alternative to `stdin`;
    passed to `subprocess.run`
  * `stdin`: standard input for the subprocess, default `subprocess.DEVNULL`;
    passed to `subprocess.run`
  * `subp_options`: optional mapping of keyword arguments
    to pass to `subprocess.run`

  Note that `argv` is passed through `prep_argv` before use,
  allowing direct invocation with conditional parts.
  See the `prep_argv` function for details.



  Context manager to push a new signal handler,
  yielding the old handler,
  restoring the old handler on exit.
  If `call_previous` is true (default `False`)
  also call the old handler after the new handler on receipt of the signal.

  Parameters:
  * `sig`: the `int` signal number to catch
  * `handler`: the handler function to call with `(sig,frame)`
  * `call_previous`: optional flag (default `False`);
    if true, also call the old handler (if any) after `handler`



  Context manager to stack multiple signal handlers,
  yielding a mapping of `sig`=>`old_handler`.

  Parameters:
  * `sig_hnds`: a mapping of `sig`=>`new_handler`
    or an iterable of `(sig,new_handler)` pairs
  * `call_previous`: optional flag (default `False`), passed
    to `signal_handler()`



  Stop the process specified by `pid`, optionally await its demise.

  Parameters:
  * `pid`: process id.
    If `pid` is a string, treat as a process id file and read the
    process id from it.
  * `signum`: the signal to send, default `signal.SIGTERM`.
  * `wait`: whether to wait for the process, default `None`.
    If `None`, return `True` (signal delivered).
    If `0`, wait indefinitely until the process exits as tested by
    `os.kill(pid, 0)`.
    If greater than 0, wait up to `wait` seconds for the process to die;
    if it exits, return `True`, otherwise `False`;
  * `do_SIGKILL`: if true (default `False`),
    send the process `signal.SIGKILL` as a final measure before return.



  Write a process id to a pid file.

  Parameters:
  * `path`: the path to the pid file.
  * `pid`: the process id to write, defautl from `os.getpid`.





  *Release 20241122*:
  * print_argv: new print= parameter to provide a print() function, refactor to use print instead of file.write.
  * run: new optional print parameter, plumb to print_argv.
  * Use @uses_doit and @uses_quiet to provide the default quiet and doit states.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240211*:
  * run: new optional input= parameter to presupply input data.
  * New prep_argv(*argv) function to flatten and trim computes argv lists; use automatically in run(), pipeot(), pipefrom().

  *Release 20231129*:
  run(): default stdin=subprocess.DEVNULL.

  *Release 20230612*:
  * pipefrom: default stdin=DEVNULL.
  * Make many parameters keyword only.

  *Release 20221228*:
  * signal_handlers: bugfix iteration of sig_hnds.
  * Use cs.gimmicks instead of cs.logutils.
  * Drop use of cs.upd, fixes circular import; users of run() may need to call "with Upd().above()" themselves.

  *Release 20221118*:
  run: do not print cp.stdout.

  *Release 20220805*:
  run: print trace to stderr.

  *Release 20220626*:
  run: default quiet=True.

  *Release 20220606*:
  * run: fold in the superior run() from cs.ebooks.
  * pipefrom,pipeto: replace trace= with quiet= like run().
  * print_argv: add an `end` parameter (used by pipefrom).

  *Release 20220531*:
  * New print_argv function for writing shell command lines out nicely.
  * Bump requirements for cs.gimmicks.

  *Release 20220504*:
  signal_handlers: reshape try/except to avoid confusing traceback.

  *Release 20220429*:
  * New signal_handler(sig,handler,call_previous=False) context manager to push a signal handler.
  * New signal_handlers() context manager to stack handlers for multiple signals.

  *Release 20190101*:
  Bugfix context manager cleanup. groupargv improvements.

  *Release 20171112*:
  Bugfix array length counting.

  *Release 20171110*:
  New function groupargv for breaking up argv lists to fit within the maximum argument limit; constant MAX_ARGV for the default limit.

  *Release 20171031*:
  run: accept optional pids parameter, a setlike collection of process ids.

  *Release 20171018*:
  run: replace `trace` parameter with `logger`, default None

  *Release 20170908.1*:
  remove dependency on cs.pfx

  *Release 20170908*:
  run: pass extra keyword arguments to subprocess.call

  *Release 20170906.1*:
  Add run, pipefrom and pipeto - were incorrectly in another module.

  *Release 20170906*:
  First PyPI release.


pkgId: 623ec1400cae38b8aab81f93b86eeee4678cb7d0f0bdf8defd48b9f48023352e
name: python-cs-psutils
arch: src
version: 20241206
epoch: 0
release: 1
summary: Assorted process and subprocess management functions.
url: https://pypi.org/project/cs-psutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-psutils-20241206-1.src.rpm
checksum_type: sha256
description:
  Assorted process and subprocess management functions.

  *Latest release 20241206*:
  run(): accept new remote= and ssh_exe= parameters to support remote execution, default via @uses_cmd_options(ssh_exe).

  Not to be confused with the excellent
  (psutil)[https://pypi.org/project/psutil/] package.



  Distribute the array `argv` over multiple arrays
  to fit within `MAX_ARGV`.
  Return a list of argv lists.

  Parameters:
  * `pre_argv`: the sequence of leading arguments
  * `argv`: the sequence of arguments to distribute; this may not be empty
  * `post_argv`: optional, the sequence of trailing arguments
  * `max_argv`: optional, the maximum length of each distributed
    argument list, default from `MAX_ARGV`: `131072`
  * `encode`: default `False`.
    If true, encode the argv sequences into bytes for accurate tallying.
    If `encode` is a Boolean,
    encode the elements with their .encode() method.
    If `encode` is a `str`, encode the elements with their `.encode()`
    method with `encode` as the encoding name;
    otherwise presume that `encode` is a callable
    for encoding each element.

  The returned argv arrays will contain the encoded element values.



  Context manager for a pid file.

  Parameters:
  * `path`: the path to the process id file.
  * `pid`: the process id to store in the pid file,
    default from `os.etpid`.

  Writes the process id file at the start
  and removes the process id file at the end.



  Pipe text (usually) from a command using `subprocess.Popen`.
  Return the `Popen` object with `.stdout` as a pipe.

  Parameters:
  * `argv`: the command argument list
  * `quiet`: optional flag, default `False`;
    if true, print the command to `stderr`
  * `text`: optional flag, default `True`; passed to `Popen`.
  * `stdin`: optional value for `Popen`'s `stdin`, default `DEVNULL`
  Other keyword arguments are passed to `Popen`.

  Note that `argv` is passed through `prep_argv` before use,
  allowing direct invocation with conditional parts.
  See the `prep_argv` function for details.



  Pipe text to a command.
  Optionally trace invocation.
  Return the Popen object with .stdin encoded as text.

  Parameters:
  * `argv`: the command argument list
  * `trace`: if true (default `False`),
    if `trace` is `True`, recite invocation to stderr
    otherwise presume that `trace` is a stream
    to which to recite the invocation.

  Other keyword arguments are passed to the `io.TextIOWrapper`
  which wraps the command's input.

  Note that `argv` is passed through `prep_argv` before use,
  allowing direct invocation with conditional parts.
  See the `prep_argv` function for details.



  A trite list comprehension to reduce an argument list `*argv`
  to the entries which are not `None` or `False`
  and to flatten other entries which are not strings.

  This exists ease the construction of argument lists
  with methods like this:

      >>> command_exe = 'hashindex'
      >>> hashname = 'sha1'
      >>> quiet = False
      >>> verbose = True
      >>> prep_argv(
      ...     command_exe,
      ...     quiet and '-q',
      ...     verbose and '-v',
      ...     hashname and ('-h', hashname),
      ... )
      ['hashindex', '-v', '-h', 'sha1']

  where `verbose` is a `bool` governing the `-v` option
  and `hashname` is either `str` to be passed with `-h hashname`
  or `None` to omit the option.

  If `remote` is not `None` it is taken to be a remote host on
  which to run `argv`. This is done via the `ssh_exe` argument,
  which defaults to the string `'ssh'`. The value of `ssh_exe`
  is a command string parsed with `shlex.split`. A new `argv`
  is computed as:

      [
          *shlex.split(ssh_exe),
          remote,
          '--',
          shlex.join(argv),
      ]



  Print an indented possibly folded command line.



  Truncate and remove a pidfile, permissions permitting.



  Run a command via `subprocess.run`.
  Return the `CompletedProcess` result or `None` if `doit` is false.

  Positional parameter:
  * `argv`: the command line to run

  Note that `argv` is passed through `prep_argv(argv,remote=remote,ssh_exe=ssh_exe)`
  before use, allowing direct invocation with conditional parts.
  See the `prep_argv` function for details.

  Keyword parameters:
  * `check`: passed to `subprocess.run`, default `True`;
    NB: _unlike_ the `subprocess.run` default, which is `False`
  * `doit`: optional flag, default `True`;
    if false do not run the command and return `None`
  * `input`: default `None`: alternative to `stdin`;
    passed to `subprocess.run`
  * `logger`: optional logger, default `None`;
    if `True`, use `logging.getLogger()`;
    if not `None` or `False` trace using `print_argv`
  * `quiet`: default `False`; if false, print the command and its output
  * `remote`: optional remote target on which to run `argv`
  * `ssh_exe`: optional command string for the remote shell
  * `stdin`: standard input for the subprocess, default `subprocess.DEVNULL`;
    passed to `subprocess.run`

  Other keyword parameters are passed to `subprocess.run`.



  Context manager to push a new signal handler,
  yielding the old handler,
  restoring the old handler on exit.
  If `call_previous` is true (default `False`)
  also call the old handler after the new handler on receipt of the signal.

  Parameters:
  * `sig`: the `int` signal number to catch
  * `handler`: the handler function to call with `(sig,frame)`
  * `call_previous`: optional flag (default `False`);
    if true, also call the old handler (if any) after `handler`



  Context manager to stack multiple signal handlers,
  yielding a mapping of `sig`=>`old_handler`.

  Parameters:
  * `sig_hnds`: a mapping of `sig`=>`new_handler`
    or an iterable of `(sig,new_handler)` pairs
  * `call_previous`: optional flag (default `False`), passed
    to `signal_handler()`



  Stop the process specified by `pid`, optionally await its demise.

  Parameters:
  * `pid`: process id.
    If `pid` is a string, treat as a process id file and read the
    process id from it.
  * `signum`: the signal to send, default `signal.SIGTERM`.
  * `wait`: whether to wait for the process, default `None`.
    If `None`, return `True` (signal delivered).
    If `0`, wait indefinitely until the process exits as tested by
    `os.kill(pid, 0)`.
    If greater than 0, wait up to `wait` seconds for the process to die;
    if it exits, return `True`, otherwise `False`;
  * `do_SIGKILL`: if true (default `False`),
    send the process `signal.SIGKILL` as a final measure before return.



  Write a process id to a pid file.

  Parameters:
  * `path`: the path to the pid file.
  * `pid`: the process id to write, defautl from `os.getpid`.





  *Release 20241206*:
  run(): accept new remote= and ssh_exe= parameters to support remote execution, default via @uses_cmd_options(ssh_exe).

  *Release 20241122*:
  * print_argv: new print= parameter to provide a print() function, refactor to use print instead of file.write.
  * run: new optional print parameter, plumb to print_argv.
  * Use @uses_doit and @uses_quiet to provide the default quiet and doit states.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240211*:
  * run: new optional input= parameter to presupply input data.
  * New prep_argv(*argv) function to flatten and trim computes argv lists; use automatically in run(), pipeot(), pipefrom().

  *Release 20231129*:
  run(): default stdin=subprocess.DEVNULL.

  *Release 20230612*:
  * pipefrom: default stdin=DEVNULL.
  * Make many parameters keyword only.

  *Release 20221228*:
  * signal_handlers: bugfix iteration of sig_hnds.
  * Use cs.gimmicks instead of cs.logutils.
  * Drop use of cs.upd, fixes circular import; users of run() may need to call "with Upd().above()" themselves.

  *Release 20221118*:
  run: do not print cp.stdout.

  *Release 20220805*:
  run: print trace to stderr.

  *Release 20220626*:
  run: default quiet=True.

  *Release 20220606*:
  * run: fold in the superior run() from cs.ebooks.
  * pipefrom,pipeto: replace trace= with quiet= like run().
  * print_argv: add an `end` parameter (used by pipefrom).

  *Release 20220531*:
  * New print_argv function for writing shell command lines out nicely.
  * Bump requirements for cs.gimmicks.

  *Release 20220504*:
  signal_handlers: reshape try/except to avoid confusing traceback.

  *Release 20220429*:
  * New signal_handler(sig,handler,call_previous=False) context manager to push a signal handler.
  * New signal_handlers() context manager to stack handlers for multiple signals.

  *Release 20190101*:
  Bugfix context manager cleanup. groupargv improvements.

  *Release 20171112*:
  Bugfix array length counting.

  *Release 20171110*:
  New function groupargv for breaking up argv lists to fit within the maximum argument limit; constant MAX_ARGV for the default limit.

  *Release 20171031*:
  run: accept optional pids parameter, a setlike collection of process ids.

  *Release 20171018*:
  run: replace `trace` parameter with `logger`, default None

  *Release 20170908.1*:
  remove dependency on cs.pfx

  *Release 20170908*:
  run: pass extra keyword arguments to subprocess.call

  *Release 20170906.1*:
  Add run, pipefrom and pipeto - were incorrectly in another module.

  *Release 20170906*:
  First PyPI release.


pkgId: 092899879ea2b413c4fb413c4da8c06fb6c2961cf018479ca69c77c13162c444
name: python-cs-py-modules
arch: src
version: 20241122
epoch: 0
release: 1
summary: Convenience functions related to modules and importing.
url: https://pypi.org/project/cs-py-modules/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-py-modules-20241122-1.src.rpm
checksum_type: sha256
description:
  Convenience functions related to modules and importing.

  *Latest release 20241122*:
  import_module_name: rename the `path` parameter to `sys_path` to match import_module_from_file, default sys_path to sys.path.



  Crudely parse `src_filename` for `import` statements.
  Return the set of directly imported module names.

  If `module_name` is not `None`,
  resolve relative imports against it.
  Otherwise, relative import names are returned unresolved.

  This is a very simple minded source parse.



  Try to import the package named `extra_package_name`
  using `importlib.import_module`. Return the imported package.

  If an `ImportError` is raised,
  riffle through the extras mapping in `distinfo['extras_requires']`
  for the package name, and emit an informative warning
  about the extras which require this package
  and whose use a `pip install` time would bring the package in.
  The original `ImportError` is then reraised.

  If no extra is found this is presumed to be an error by the caller
  and a `RuntimeError` is raised.
  This function is for internal use as:

      pkg = import_extra('some_package', DISTINFO)

  which passes in the source module's `DISTINFO` mapping,
  which I use as the basis for my package distributions.

  A fuller example from my `cs.timeseries` module's
  `plot` command line mode:

      def cmd_plot(self, argv):
        """ Usage: {cmd} datadir days fields...
        """
        try:
          import_extra('plotly', DISTINFO)
        except ImportError as e:
          raise GetoptError(
              "the plotly package is not installed: %s" % (e,)
          ) from e

  which produces this output:

      timeseries.py: plot: import_extra('plotly'): package not available; the following extras pull it in: ['plotting']
      timeseries.py: the plotly package is not installed: timeseries.py: plot: import_extra('plotly'): No module named 'plotly'



  Import a specific file as a module instance,
  return the module instance.

  Parameters:
  * `module_name`: the name to assign to the module
  * `source_file`: the source file to load
  * `sys_path`: optional list of paths to set as `sys.path`
    for the duration of this import;
    the default is the current value of `sys.path`

  Note that this is a "bare" import;
  the module instance is not inserted into `sys.modules`.

  *Warning*: `sys.path` is modified for the duration of this function,
  which may affect multithreaded applications.



  Import `module_name` and return the value of `name` within it.

  Parameters:
  * `module_name`: the module name to import.
  * `name`: optional name within the module whose value is returned;
    if `name` is `None`, return the module itself.
  * `sys_path`optional list array of paths to use as `sys.path` during the import.
  * `lock`: an optional lock to hold during the import (recommended).



  Generator yielding the names and values of attributes from a module
  which were defined in the module.



  Generator yielding `.py` pathnames involved in a module.



  Return a list of the names of attributes from a module which were
  defined in the module.





  *Release 20241122*:
  import_module_name: rename the `path` parameter to `sys_path` to match import_module_from_file, default sys_path to sys.path.

  *Release 20240630*:
  direct_imports: fix off-by-one resolving leading dot relative import names.

  *Release 20220606*:
  New import_extra(extra_package_name,distinfo) function to politely try to import a package which is associated with an extra.

  *Release 20210123*:
  module_attributes: skip values from other modules _if we know the module_ (computed values like tuples have no module and still need to be returned).

  *Release 20200521*:
  * New import_module_from_file function to import a Python file as a module instance.
  * New direct_imports(src_filename,module_name=None) returning the set of directly imports module names.

  *Release 20190101*:
  New functions: module_names, module_attributes.

  *Release 20160918*:
  * New generator function module_files yielding pathnames.
  * import_module_name: accept name=None, just return the module.
  * Add empty "install_requires" for DISTINFO completeness.

  *Release 20150116*:
  Initial PyPI release.


pkgId: 28c6ba7d835e981fa78f6a60387c5ffe4317d55054c0331ed6bdd2a91f1d72ac
name: python-cs-sqlalchemy-utils
arch: src
version: 20241122
epoch: 0
release: 1
summary: Assorted utility functions to support working with SQLAlchemy.
url: https://pypi.org/project/cs-sqlalchemy-utils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-sqlalchemy-utils-20241122-1.src.rpm
checksum_type: sha256
description:
  Assorted utility functions to support working with SQLAlchemy.

  *Latest release 20241122*:
  * RelationProxy: __getitem__ now raises KeyError, __getattr__ now raises AttributeError.
  * RelationProxy: new optional "missing" parameter to provide a callable for field values when there is no matching record in the relation.



  Decorator to run a function in a session if one is not presupplied.
  The function `function` runs within a transaction,
  nested if the session already exists.

  See `with_session` for details.



  Useful methods for most tables.

  *`BasicTableMixin.__getitem__(index, *, id_column=None, session)`*:
  Index the table by its `id_column` column, default `'id'`.

  *`BasicTableMixin.by_id(index, *, id_column=None, session)`*:
  Index the table by its `id_column` column, default `'id'`.

  *`BasicTableMixin.lookup(*, session, **criteria)`*:
  Return an iterable `Query` of row entities matching `criteria`.

  *`BasicTableMixin.lookup1(*, session, **criteria)`*:
  Return the row entity matching `criteria`, or `None` if no match.



  Descend a JSONable Python object `column_value`
  to `field_name`.
  Return `column_value` (possibly infilled), `final_field`, `final_field_name`.

  This supports database row columns which are JSON columns.

  Parameters:
  * `column_value`: the original value of the column
  * `field_name`: the field within the column to locate
  * `infill`: optional keyword parameter, default `False`.
    If true,
    `column_value` and its innards will be filled in as `dict`s
    to allow deferencing the `field_name`.

  The `field_name` is a `str`
  consisting of a period (`'.'`) separated sequence of field parts.
  Each field part becomes a key to index the column mapping.
  These keys are split into the leading field parts
  and the final field part,
  which is returned as `final_field_name` above.

  The `final_field` return value above
  is the mapping within which `final_field_value` may lie
  and where `final_field_value` may be set.
  Note: it may not be present.

  If a leading key is missing and `infill` is true
  the corresponding part of the `column_value` is set to an empty dictionary
  in order to allow deferencing the leading key.
  This includes the case when `column_value` itself is `None`,
  which is why the `column_value` is part of the return.

  If a leading key is missing and `infill` is false
  this function will raise a `KeyError`
  for the portion of the `field_name` which failed.

  Examples:

      >>> find_json_field({'a':{'b':{}}}, 'a.b')
      ({'a': {'b': {}}}, {'b': {}}, 'b')
      >>> find_json_field({'a':{}}, 'a.b')
      ({'a': {}}, {}, 'b')
      >>> find_json_field({'a':{'b':{}}}, 'a.b.c.d')
      Traceback (most recent call last):
          ...
      KeyError: 'a.b.c'
      >>> find_json_field({'a':{'b':{}}}, 'a.b.c.d', infill=True)
      ({'a': {'b': {'c': {}}}}, {}, 'd')
      >>> find_json_field(None, 'a.b.c.d')
      Traceback (most recent call last):
          ...
      KeyError: 'a'
      >>> find_json_field(None, 'a.b.c.d', infill=True)
      ({'a': {'b': {'c': {}}}}, {}, 'd')



  Return the value of `field_name` from `column_value`
  or a defaault if the field is not present.

  Parameters:
  * `column_value`: the original value of the column
  * `field_name`: the field within the column to locate
  * `default`: default value to return if the field is not present,
    default: `None`

  Examples:

      >>> get_json_field({'a': 1}, 'a')
      1
      >>> get_json_field({'b': 1}, 'a')
      >>> get_json_field({'a': {}}, 'a.b')
      >>> get_json_field({'a': {'b': 2}}, 'a.b')
      2



  Include an "id" `Column` as the primary key.



  Class decorator to declare a virtual column name on a table
  where the value resides inside a JSON column of the table.

  Parameters:
  * `cls`: the class to annotate
  * `attr`: the virtual column name to present as a row attribute
  * `json_field_name`: the field within the JSON column
    used to store this value,
    default the same as `attr`
  * `json_column_name`: the name of the associated JSON column,
    default `'info'`
  * `default`: the default value returned by the getter
    if the field is not present,
    default `None`

  Example use:

      Base = declarative_base()
      ...
      @json_column('virtual_name', 'json.field.name')
      class TableClass(Base):
        ...

  This annotates the class with a `.virtual_name` property
  which can be accessed or set,
  accessing or modifying the associated JSON column
  (in this instance, the column `info`,
  accessing `info['json']['field']['name']`).



  Temporarily set the level of the default SQLAlchemy logger to `level`.
  Yields the logger.

  *NOTE*: this is not MT safe - competing Threads can mix log levels up.



  A convenience base class for an ORM class.

  This defines a `.Base` attribute which is a new `DeclarativeBase`
  and provides various Session related convenience methods.
  It is also a `MultiOpenMixin` subclass
  supporting nested open/close sequences and use as a context manager.

  *`ORM.__init__(self, db_url, serial_sessions=None)`*:
  Initialise the ORM.

  If `serial_sessions` is true (default `True` for SQLite, `False` otherwise)
  then allocate a lock to serialise session allocation.
  This might be chosen with SQL backends which do not support
  concurrent sessions such as SQLite.

  In the case of SQLite there's a small inbuilt timeout in
  an attempt to serialise transactions but it is possible to
  exceed it easily and recovery is usually infeasible.
  Instead we use the `serial_sessions` option to obtain a
  mutex before allocating a session.

  *`ORM.declare_schema(self)`*:
  Declare the database schema / ORM mapping.
  This just defines the relation types etc.
  It *does not* act on the database itself.
  It is called automatically at the end of `__init__`.

  Example:

      def declare_schema(self):
        """ Define the database schema / ORM mapping.
        """
        orm = self
        Base = self.Base
        class Entities(
        ........
        self.entities = Entities

  After this, methods can access the example `Entities` relation
  as `self.entites`.

  *`ORM.default_session`*:
  The current per-`Thread` session.

  *`ORM.engine`*:
  SQLAlchemy engine, made on demand.

  *`ORM.orchestrated_session(self)`*:
  Orchestrate a new session for this `Thread`,
  honouring `self.serial_session`.

  *`ORM.startup_shutdown(self)`*:
  Default startup/shutdown context manager.

  This base method operates a lockfile to manage concurrent access
  by other programmes (which would also need to honour this file).
  If you actually expect this to be common
  you should try to keep the `ORM` "open" as briefly as possible.
  The lock file is only operated if `self.db_fspath`,
  currently set only for filesystem SQLite database URLs.



  Decorator to run a method in a session derived from `self.orm`
  if a session is not presupplied.
  Intended to assist classes with a `.orm` attribute.

  See `with_session` for details.



  A decorator to provide a field value on demand
  via a function `field_func(self,db_row,session=session)`.

  Example:

      @property
      @proxy_on_demand_field
      def formats(self,db_row,*,session):
          """ A mapping of Calibre format keys to format paths
              computed on demand.
          """
          return {
              fmt.format:
              joinpath(db_row.path, f'{fmt.name}.{fmt.format.lower()}')
              for fmt in db_row.formats
          }



  Construct a proxy for a row from a relation.

  Parameters:
  * `relation`: an ORM relation for which this will be a proxy
  * `columns`: a list of the column names to cache,
    or a space separated string of the column names
  * `id_column`: options primary key column name,
    default from `BasicTableMixin.DEFAULT_ID_COLUMN`: `'id'`
  * `orm`: the ORM, default from `relation.orm`
  * `missing`: an optional function to produce a default value
    for a field if there is no matching record in the relation;
    if `None` (the default) access to a field raises `KeyError`
    or `AttributeError` as appropriate

  This is something of a workaround for applications which dip
  briefly into the database to obtain information instead of
  doing single long running transactions or sessions.
  Instead of keeping the row instance around, which might want
  to load related data on demand after its source session is
  expired, we keep a proxy for the row with cached values
  and refetch the row at need if further information is required.

  Typical use is to construct this proxy class as part
  of the `__init__` of a larger class which accesses the database
  as part of its operation.
  The example below is based on `cs.ebooks.calibre.CalibreTree`:

      def __init__(self, calibrepath):
        super().__init__(calibrepath)
        
        class CalibreBook(RelationProxy(self.db.books, [
            'author',
            'title',
        ])):
          """ A reference to a book in a Calibre library.
          """
          @typechecked
          def __init__(self, tree: CalibreTree, dbid: int, db_book=None):
            self.tree = tree
            self.dbid = dbid
          ... various other CalibreBook methods ...
        self.CalibreBook = CalibreBook

      def __getitem__(self, dbid):
        return self.CalibreBook(self, dbid, db_book=db_book)



  Set a new `value` for `field_name` of `column_value`.
  Return the new `column_value`.

  Parameters:
  * `column_value`: the original value of the column
  * `field_name`: the field within the column to locate
  * `value`: the value to store as `field_name`
  * `infill`: optional keyword parameter, default `False`.
    If true,
    `column_value` and its innards will be filled in as `dict`s
    to allow deferencing the `field_name`.

  As with `find_json_field`,
  a true `infill` may modify `column_value` to provide `field_name`
  which is why this function returns the new `column_value`.

  Examples:

      >>> set_json_field({'a': 2}, 'a', 3)
      {'a': 3}
      >>> set_json_field({'a': 2, 'b': {'c': 5}}, 'b.c', 4)
      {'a': 2, 'b': {'c': 4}}
      >>> set_json_field({'a': 2}, 'b.c', 4)
      Traceback (most recent call last):
          ...
      KeyError: 'b'
      >>> set_json_field({'a': 2}, 'b.c', 4, infill=True)
      {'a': 2, 'b': {'c': 4}}
      >>> set_json_field(None, 'b.c', 4, infill=True)
      {'b': {'c': 4}}



  Thread local state for SQLAlchemy ORM and session.

  *`SQLAState.auto_session(self, *, orm=None)`*:
  Context manager to use the current session
  if not `None`, otherwise to make one using `orm` or `self.orm`.

  *`SQLAState.new_session(self, *, orm=None)`*:
  Context manager to create a new session from `orm` or `self.orm`.



  A context manager to prepare an SQLAlchemy session
  for use by a suite.

  Parameters:
  * `orm`: optional reference ORM,
    an object with a `.session()` method for creating a new session.
    Default: if needed, obtained from the global `state.orm`.
  * `session`: optional existing session.
    Default: the global `state.session` if not `None`,
    otherwise created by `orm.session()`.

  If a new session is created, the new session and reference ORM
  are pushed onto the globals `state.session` and `state.orm`
  respectively.

  If an existing session is reused,
  the suite runs within a savepoint from `session.begin_nested()`.



  Call `function` with the supplied `orm` in the shared state.



  Call `function(*a,session=session,**kw)`, creating a session if required.
  The function `function` runs within a transaction,
  nested if the session already exists.
  If a new session is created
  it is set as the default session in the shared state.

  This is the inner mechanism of `@auto_session` and
  `ORM.auto_session`.

  Parameters:
  * `function`: the function to call
  * `a`: the positional parameters
  * `orm`: optional ORM class with a `.session()` context manager method
    such as the `ORM` base class supplied by this module.
  * `session`: optional existing ORM session
  * `kw`: other keyword arguments, passed to `function`

  One of `orm` or `session` must be not `None`; if `session`
  is `None` then one is made from `orm.session()` and used as
  a context manager.

  The `session` is also passed to `function` as
  the keyword parameter `session` to support nested calls.





  *Release 20241122*:
  * RelationProxy: __getitem__ now raises KeyError, __getattr__ now raises AttributeError.
  * RelationProxy: new optional "missing" parameter to provide a callable for field values when there is no matching record in the relation.

  *Release 20241005*:
  ORM.__init__: small bugfix.

  *Release 20240723*:
  ORM: run self.Base.metadata.create_all() on the first use of the db.

  *Release 20230612*:
  * Use cs.fileutils.lockfile context manager instead of makelockfile.
  * Rename arranged_session to orchestrated_session.
  * Some tweaks around connection closes, still edging towards a good work practice for easily doing short lived stuff (for cs.ebooks.calibre).

  *Release 20230212*:
  ORM.__init__: drop case_sensitive, no longer supported?

  *Release 20220606*:
  * BasicTableMixin: provide DEFAULT_ID_COLUMN='id', by_id() has new optional id_column parameter.
  * RelationProxy factory to make base classes which proxy a relation, for circumstances where you want to minimise access to the db itself.
  * ORM.engine_keywords: turn on echo mode only if "SQL" in $DEBUG.

  *Release 20220311*:
  Many updates and small fixes.

  *Release 20210420*:
  * ORM: drop .Session from docstring, no longer used.
  * Rename ORM.sessionmaker to ORM._sessionmaker, not for public use.
  * ORM: replace session with arranged_session, which allocates a session in conformance with ORM.serial_sessions (serial sessions are used with SQLite).
  * Drop @ORM.auto_session and @ORM.orm_method decorators, no longer used.
  * SQLAState.new_session: use orm.arranged_session(), use begin_nested(); SQLAState.auto_session: use begin_nested().

  *Release 20210322*:
  Delete escaped debug code which issued a RuntimeError.

  *Release 20210321*:
  * Default session support, particularly though an ORM's .sqla_state per-Thread state object - this allows removal of a lot of plumbing and @auto_session decoration.
  * Support for serialised sessions, for db backend where only one session may be active at a time; this brings easy support for multithreaded SQLite access.

  *Release 20210306*:
  * Rename _state to state, making it public.
  * Some other internal changes.

  *Release 20201025*:
  * New BasicTableMixin and HasIdMixin classes with useful methods and a typical `id` Column respectively.
  * Assorted fixes and improvements.

  *Release 20190830.1*:
  Have the decorators set .__module__.

  *Release 20190830*:
  @json_column: small docstring improvement.

  *Release 20190829*:
  * Bugfix @json_column setter: mark the column as modified for the ORM.
  * New push_log_level context manager and @log_level decorator to temporarily change the SQLAlchemy logging handler level.

  *Release 20190812*:
  * Make ORM a MultiOpenMixin.
  * get_json_field: use forgotten `default` parameter.
  * Other minor changes.

  *Release 20190526*:
  * Support for virtual columns mapped to a JSON column interior value:
  * New functions find_json_field, get_json_field, set_json_field.
  * New decorator @json_column for declaritive_base tables.

  *Release 20190517*:
  * Make ORM._Session private session factory the public ORM.Session factory for external use.
  * with_session: preexisting sessions still trigger a session.begin_nested, removes flush/commit tension elsewhere.

  *Release 20190403*:
  * Rename @ORM.orm_auto_session to @ORM.auto_session.
  * New @orm_auto_session decorator for methods of objects with a .orm attribute.

  *Release 20190319.1*:
  Initial release. ORM base class, @auto_session decorator.


pkgId: 2145083936fe76fbc966bbe48e8c0f1fd64feab79b5305dbf32c6f00e9b33e90
name: python-cs-storage
arch: src
version: 1.11.1
epoch: 0
release: 1
url: https://github.com/compute-tooling/compute-studio-storage
rpm_license: GNU Affero General Public License v3
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-storage-1.11.1-1.src.rpm
checksum_type: sha256
summary:
  A small package that is used by Compute Studio to read and write model results to google cloud storage.

description:
  A light-weight package that is used by [Compute Studio](https://compute.studio) to read and write model results to Google Cloud Storage.



  ```bash
  pip install cs-storage
  export BUCKET=YOUR_BUCKET
  ```



  ```bash
  gcloud auth login
  gcloud auth application-default login
  ```



  ```python
  import cs_storage


  local_result, task_id = run_model(**kwargs)
  remote_result = cs_storage.write(task_id, local_result)
  round_trip = cs_storage.read(remote_result)
  assert local_result == round_trip
  ```



  ```bash
  py.test -v
  ```


pkgId: 947eec14ef3b8b8e53092ffd67a9b3e86040a8a03950c245f1a6a74e69131208
name: python-cs-util
arch: src
version: 0.1.1
epoch: 0
release: 1
summary: Utility library for CosmoStat
url: https://pypi.org/project/cs-util/
rpm_license: None
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs-util-0.1.1-1.src.rpm
checksum_type: sha256
description:
  > Author: <a href="www.cosmostat.org/people/kilbinger/" target="_blank" style="text-decoration:none; color: #F08080">Martin Kilbinger</a>
  > Email: <a href="mailto:martin.kilbinger@cea.fr" style="text-decoration:none; color: #F08080">martin.kilbinger@cea.fr</a>


pkgId: dedd33ebbc21a863e6c4d62dfa3ffd1a0c74a08451b692da5b6784c22f88ccd2
name: python-cs.ansi-colour
arch: src
version: 20220227
epoch: 0
release: 1
summary: Convenience functions for ANSI terminal colour sequences [color].
url: https://bitbucket.org/cameron_simpson/css/commits/all
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.ansi-colour-20220227-1.src.rpm
checksum_type: sha256
description:
  Convenience functions for ANSI terminal colour sequences [color].

  *Latest release 20220227*:
  * New TerminalColors class parsing /etc/terminal-colors.d and honouring $*_COLORS.
  * New colour_escape(code) to make the escape sequence for a colour code.
  * New COLOUR_CODES mapping of known colour names.

  Mapping and function for adding ANSI terminal colour escape sequences
  to strings for colour highlighting of output.



  Return the ANSI escape sequence to activate the colour `code`.
  `code` may be an `int` or a `str` which indexes `COLOUR_CODES`.



  Return a string enclosed in colour-on and colour-off ANSI sequences.

  * `colour`: names the desired ANSI colour.
  * `uncolour`: may be used to specify the colour-off colour;
    the default is 'normal' (from `NORMAL_COLOUR`).



  Colourise a string `s` according to `patterns`.

  Parameters:
  * `s`: the string.
  * `patterns`: a sequence of patterns.
  * `default_colour`: if a string pattern has no colon, or starts
    with a colon, use this colour;
    default "cyan" (from `DEFAULT_HIGHLIGHT`).

  Each pattern may be:
  * a string of the form "[colour]:regexp"
  * a string containing no colon, taken to be a regexp
  * a tuple of the form `(colour,regexp)`
  * a regexp object

  Returns the string with ANSI colour escapes embedded.



  Test the `$NO_COLOR` environment variable per the specification at
  https://no-color.org/



  Convert a `pattern` specification into a `(colour,regexp)` tuple.

  Parameters:
  * `pattern`: the pattern to parse
  * `default_colour`: the highlight colour,
    default "cyan" (from `DEFAULT_HIGHLIGHT`).

  Each `pattern` may be:
  * a string of the form "[colour]:regexp"
  * a string containing no colon, taken to be a regexp
  * a tuple of the form `(colour,regexp)`
  * a regexp object



  Convert an iterable of pattern specifications into a list of
  `(colour,regexp)` tuples.

  Parameters:
  * `patterns`: an iterable of patterns to parse
  * `default_colour`: the highlight colour,
    default "cyan" (from `DEFAULT_HIGHLIGHT`).

  Each pattern may be:
  * a string of the form "[colour]:regexp"
  * a string containing no colon, taken to be a regexp
  * a tuple of the form (colour, regexp)
  * a regexp object



  A parser for `/etc/terminal-colors.d'` files.

  *Method `TerminalColors.__init__(self, util_name=None, term_name=None, type_name=None, colors_dirpath=None, envvar=None)`*:
  Initialise the `TerminalColors` instance.

  Parameters:
  * `util_name`: optional utility name, default from `sys.argv[0]`
  * `term_name`: optional terminal name, default from the `$TERM` envvar
  * `type_name`: optional type name, default `'enable'`
  * `colors_dirpath`: optional specification files directory path,
    default from `TerminalColors.TERMINAL_COLORS_D`
  * `envvar`: environment variable to override matches;
    the default `util_name+'_COLORS'`,
    thus `$LS_COLORS` if `util_name=='ls'`.
    That may be the value `False` if no environment variable should be an override.

  *Method `TerminalColors.convert_sequence(sequence)`*:
  Convert a colour specification to an escape sequence.

  *Method `TerminalColors.find_specfile(self)`*:
  Locate the most specific specification file matching our criteria.
  Return `None` if no file matches.

  *Property `TerminalColors.mapping`*:
  The mapping of `name` to escape sequence.

  *Method `TerminalColors.scan(self, path=None)`*:
  Scan the colour specification in `path`
  and yield `(name,escape_sequence)` tuples.





  *Release 20220227*:
  * New TerminalColors class parsing /etc/terminal-colors.d and honouring $*_COLORS.
  * New colour_escape(code) to make the escape sequence for a colour code.
  * New COLOUR_CODES mapping of known colour names.

  *Release 20200729*:
  * New env_no_color() test function honouring the $NO_COLOUR envvar per https://no-color.org/
  * colourise: get the "normal" default from the global NORMAL_COLOUR.

  *Release 20180726*:
  greater markdowning of docstrings

  *Release 20180725.2*:
  some markdown, still release testing

  *Release 20180725.1*:
  Trivial changes, testing release process.

  *Release 20180725*:
  rename second colourise function to colourise_patterns, was shadowing things

  *Release 20180422*:
  * New function colourise to colour strings with regexp matches.
  * New helper functions make_pattern and make_patterns to construct (colour, regexp)s from specifications.
  * New public names NORMAL_COLOUR and DEFAULT_HIGHLIGHT.

  *Release 20170902*:
  Release metadata changes.

  *Release 20160828*:
  Add PyPI category, other minor tweaks.

  *Release 20150112*:
  PyPI release.

  *Release 20150107*:
  first standalone pypi release


pkgId: 59ed0db2b7e809394b7cb107ea8471e5b6fc78769880b79e11e1740a672ecf3f
name: python-cs.binary
arch: src
version: 20240630
epoch: 0
release: 1
url: https://pypi.org/project/cs.binary/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.binary-20240630-1.src.rpm
checksum_type: sha256
summary:
  Facilities associated with binary data parsing and transcription. The classes in this module support easy parsing of binary data structures, returning instances with the binary data decoded into attributes and capable of transcribing themselves in binary form (trivially via `bytes(instance)` and also otherwise).

description:
  Facilities associated with binary data parsing and transcription.
  The classes in this module support easy parsing of binary data
  structures,
  returning instances with the binary data decoded into attributes
  and capable of transcribing themselves in binary form
  (trivially via `bytes(instance)` and also otherwise).

  *Latest release 20240630*:
  * flatten: do not yield empty str-as-ascii-bytes - we want to ensure that flatten never yields an empty bytes instance.
  * New AbstractBinary.write(f) method to write the binary form of this object to a file.
  * New BinarySingleValue.value_from_bytes(bytes) class method to return the value from a bytes instance.
  * Drop BinaryMixin, now folded directly into AbstractBinary.
  * AbstractBinary.scan: pass extra keyword arguments to AbstractBinary.parse, supporting plumbing eg a logging parameter through.

  Note: this module requires Python 3.6+ because various default
  behaviours rely on `dict`s preserving their insert order.

  See `cs.iso14496` for an ISO 14496 (eg MPEG4) parser
  built using this module.

  Terminology used below:
  * buffer:
    an instance of `cs.buffer.CornuCopyBuffer`,
    which manages an iterable of bytes-like values
    and has various useful methods;
    it also has a few factory methods to make one from a variety of sources
    such as bytes, iterables, binary files, `mmap`ped files,
    TCP data streams, etc.
  * chunk:
    a piece of binary data obeying the buffer protocol,
    almost always a `bytes` instance or a `memoryview`,
    but in principle also things like `bytearray`.

  There are 5 main classes on which an implementor should base their data structures:
  * `BinarySingleStruct`: a factory for classes based
    on a `struct.struct` format string with a single value;
    this builds a `namedtuple` subclass
  * `BinaryMultiStruct`: a factory for classes based
    on a `struct.struct` format string with multiple values;
    this also builds a `namedtuple` subclass
  * `BinarySingleValue`: a base class for subclasses
    parsing and transcribing a single value
  * `BinaryMultiValue`: a base class for subclasses
    parsing and transcribing multiple values
    with no variation
  * `SimpleBinary`: a base class for subclasses
    with custom `.parse` and `.transcribe` methods,
    for structures with variable fields

  Any the classes derived from the above inherit all the methods
  of `AbstractBinary`.
  Amongst other things, this means that the binary transcription
  can be had simply from `bytes(instance)`,
  although there are more transcription methods provided
  for when greater flexibility is desired.
  It also means that all classes have `parse`* and `scan`* methods
  for parsing binary data streams.

  You can also instantiate objects directly;
  there's no requirement for the source information to be binary.

  There are several presupplied subclasses for common basic types
  such as `UInt32BE` (an unsigned 32 bit big endian integer).



  Abstract class for all `Binary`* implementations,
  specifying the abstract `parse` and `transcribe` methods
  and providing various helper methods.

  Naming conventions:
  - `parse`* methods parse a single instance from a buffer
  - `scan`* methods are generators yielding successive instances from a buffer

  *Method `AbstractBinary.__bytes__(self)`*:
  The binary transcription as a single `bytes` object.

  *Method `AbstractBinary.__len__(self)`*:
  Compute the length by running a transcription and measuring it.

  *Method `AbstractBinary.from_bytes(bs, **parse_bytes_kw)`*:
  Factory to parse an instance from the
  bytes `bs` starting at `offset`.
  Returns the new instance.

  Raises `ValueError` if `bs` is not entirely consumed.
  Raises `EOFError` if `bs` has insufficient data.

  Keyword parameters are passed to the `.parse_bytes` method.

  This relies on the `cls.parse` method for the parse.

  *Method `AbstractBinary.load(f)`*:
  Load an instance from the file `f`
  which may be a filename or an open file as for `AbstractBinary.scan`.
  Return the instance or `None` if the file is empty.

  *Method `AbstractBinary.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse an instance of `cls` from the buffer `bfr`.

  *Method `AbstractBinary.parse_bytes(bs, offset=0, length=None, **parse_kw)`*:
  Factory to parse an instance from the
  bytes `bs` starting at `offset`.
  Returns `(instance,offset)` being the new instance and the post offset.

  Raises `EOFError` if `bs` has insufficient data.

  The parameters `offset` and `length` are passed to the
  `CornuCopyBuffer.from_bytes` factory.

  Other keyword parameters are passed to the `.parse` method.

  This relies on the `cls.parse` method for the parse.

  *Method `AbstractBinary.save(self, f)`*:
  Save this instance to the file `f`
  which may be a filename or an open file.
  Return the length of the transcription.

  *Method `AbstractBinary.scan(bfr: cs.buffer.CornuCopyBuffer, count=None, *, min_count=None, max_count=None, with_offsets=False, **parse_kw)`*:
  Function to scan the buffer `bfr` for repeated instances of `cls`
  until end of input and yield them.

  Parameters:
  * `bfr`: the buffer to scan, or any object suitable for `CornuCopyBuffer.promote`
  * `count`: the required number of instances to scan,
    equivalent to setting `min_count=count` and `max_count=count`
  * `min_count`: the minimum number of instances to scan
  * `max_count`: the maximum number of instances to scan
  * `with_offsets`: optional flag, default `False`;
    if true yield `(pre_offset,obj,post_offset)`, otherwise just `obj`
  It is in error to specify both `count` and one of `min_count` or `max_count`.

  Other keyword arguments are passed to `self.parse()`.

  Scanning stops after `max_count` instances (if specified).
  If fewer than `min_count` instances (if specified) are scanned
  a warning is issued.
  This is to accomodate nonconformant streams
  without raising exceptions.
  Callers wanting to validate `max_count` may want to probe `bfr.at_eof()`
  after return.
  Callers not wanting a warning over `min_count` should not specify it,
  and instead check the number of instances returned themselves.

  *Method `AbstractBinary.scan_fspath(fspath: str, *, with_offsets=False, **kw)`*:
  Open the file with filesystenm path `fspath` for read
  and yield from `self.scan(..,**kw)` or
  `self.scan_with_offsets(..,**kw)` according to the
  `with_offsets` parameter.

  *Deprecated; please just call `scan` with a filesystem pathname.

  Parameters:
  * `fspath`: the filesystem path of the file to scan
  * `with_offsets`: optional flag, default `False`;
    if true then scan with `scan_with_offsets` instead of
    with `scan`
  Other keyword parameters are passed to `scan` or
  `scan_with_offsets`.

  *Method `AbstractBinary.scan_with_offsets(bfr: cs.buffer.CornuCopyBuffer, count=None, min_count=None, max_count=None)`*:
  Wrapper for `scan()` which yields `(pre_offset,instance,post_offset)`
  indicating the start and end offsets of the yielded instances.
  All parameters are as for `scan()`.

  *Deprecated; please just call `scan` with the `with_offsets=True` parameter.

  *Method `AbstractBinary.self_check(self)`*:
  Internal self check. Returns `True` if passed.

  If the structure has a `FIELD_TYPES` attribute, normally a
  class attribute, then check the fields against it. The
  `FIELD_TYPES` attribute is a mapping of `field_name` to
  a specification of `required` and `types`. The specification
  may take one of 2 forms:
  * a tuple of `(required,types)`
  * a single `type`; this is equivalent to `(True,(type,))`
  Their meanings are as follows:
  * `required`: a Boolean. If true, the field must be present
    in the packet `field_map`, otherwise it need not be present.
  * `types`: a tuple of acceptable field types

  There are some special semantics involved here.

  An implementation of a structure may choose to make some
  fields plain instance attributes instead of binary objects
  in the `field_map` mapping, particularly variable structures
  such as a `cs.iso14496.BoxHeader`, whose `.length` may be parsed
  directly from its binary form or computed from other fields
  depending on the `box_size` value. Therefore, checking for
  a field is first done via the `field_map` mapping, then by
  `getattr`, and as such the acceptable `types` may include
  nonstructure types such as `int`.

  Here is the `cs.iso14496` `Box.FIELD_TYPES` definition as an example:

      FIELD_TYPES = {
          'header': BoxHeader,
          'body': BoxBody,
          'unparsed': list,
          'offset': int,
          'unparsed_offset': int,
          'end_offset': int,
      }

  Note that `length` includes some nonstructure types,
  and that it is written as a tuple of `(True,types)` because
  it has more than one acceptable type.

  *Method `AbstractBinary.transcribe(self)`*:
  Return or yield `bytes`, ASCII string, `None` or iterables
  comprising the binary form of this instance.

  This aims for maximum convenience when transcribing a data structure.

  This may be implemented as a generator, yielding parts of the structure.

  This may be implemented as a normal function, returning:
  * `None`: no bytes of data,
    for example for an omitted or empty structure
  * a `bytes`-like object: the full data bytes for the structure
  * an ASCII compatible string:
    this will be encoded with the `'ascii'` encoding to make `bytes`
  * an iterable:
    the components of the structure,
    including substranscriptions which themselves
    adhere to this protocol - they may be `None`, `bytes`-like objects,
    ASCII compatible strings or iterables.
    This supports directly returning or yielding the result of a field's
    `.transcribe` method.

  *Method `AbstractBinary.transcribe_flat(self)`*:
  Return a flat iterable of chunks transcribing this field.

  *Method `AbstractBinary.transcribed_length(self)`*:
  Compute the length by running a transcription and measuring it.

  *Method `AbstractBinary.write(self, file, *, flush=False)`*:
  Write this instance to `file`, a file-like object supporting
  `.write(bytes)` and `.flush()`.
  Return the number of bytes written.



  A list of `bytes` parsed directly from the native iteration of the buffer.



  Factory for an `AbstractBinary` subclass matching `length` bytes of data.
  The bytes are saved as the attribute `.data`.



  A list of values with a common parse specification,
  such as sample or Boxes in an ISO14496 Box structure.

  *Method `BinaryListValues.parse(bfr: cs.buffer.CornuCopyBuffer, count=None, *, end_offset=None, min_count=None, max_count=None, pt)`*:
  Read values from `bfr`.
  Return a `BinaryListValue` containing the values.

  Parameters:
  * `count`: optional count of values to read;
    if specified, exactly this many values are expected.
  * `end_offset`: an optional bounding end offset of the buffer.
  * `min_count`: the least acceptable number of values.
  * `max_count`: the most acceptable number of values.
  * `pt`: a parse/transcribe specification
    as accepted by the `pt_spec()` factory.
    The values will be returned by its parse function.

  *Method `BinaryListValues.transcribe(self)`*:
  Transcribe all the values.



  A class factory for `AbstractBinary` `namedtuple` subclasses
  built around complex `struct` formats.

  Parameters:
  * `class_name`: name for the generated class
  * `struct_format`: the `struct` format string
  * `field_names`: field name list,
    a space separated string or an interable of strings

  Example:

      
      Enigma2APInfo = BinaryMultiStruct('Enigma2APInfo', '>QQ', 'pts offset')

      
      Enigma2Cut = BinaryMultiStruct('Enigma2Cut', '>QL', 'pts type')



  Construct a `SimpleBinary` subclass named `class_name`
  whose fields are specified by the mapping `field_map`.

  The `field_map` is a mapping of field name to buffer parsers and transcribers.

  *Note*:
  if `field_order` is not specified
  it is constructed by iterating over `field_map`.
  Prior to Python 3.6, `dict`s do not provide a reliable order
  and should be accompanied by an explicit `field_order`.
  From 3.6 onward a `dict` is enough and its insertion order
  will dictate the default `field_order`.

  For a fixed record structure
  the default `.parse` and `.transcribe` methods will suffice;
  they parse or transcribe each field in turn.
  Subclasses with variable records should override
  the `.parse` and `.transcribe` methods
  accordingly.

  The `field_map` is a mapping of field name
  to a class returned by the `pt_spec()` function.

  If the class has both `parse_value` and `transcribe_value` methods
  then the value itself will be directly stored.
  Otherwise the class it presumed to be more complex subclass
  of `AbstractBinary` and the instance is stored.

  Here is an example exhibiting various ways of defining each field:
  * `n1`: defined with the *`_value` methods of `UInt8`,
    which return or transcribe the `int` from an unsigned 8 bit value;
    this stores a `BinarySingleValue` whose `.value` is an `int`
  * `n2`: defined from the `UInt8` class,
    which parses an unsigned 8 bit value;
    this stores an `UInt8` instance
    (also a `BinarySingleValue` whole `.value` is an `int`)
  * `n3`: like `n2`
  * `data1`: defined with the *`_value` methods of `BSData`,
    which return or transcribe the data `bytes`
    from a run length encoded data chunk;
    this stores a `BinarySingleValue` whose `.value` is a `bytes`
  * `data2`: defined from the `BSData` class
    which parses a run length encoded data chunk;
    this is a `BinarySingleValue` so we store its `bytes` value directly.

        >>> class BMV(BinaryMultiValue("BMV", {
        ...         'n1': (UInt8.parse_value, UInt8.transcribe_value),
        ...         'n2': UInt8,
        ...         'n3': UInt8,
        ...         'nd': ('>H4s', 'short bs'),
        ...         'data1': (
        ...             BSData.parse_value,
        ...             BSData.transcribe_value,
        ...         ),
        ...         'data2': BSData,
        ... })):
        ...     pass
        >>> BMV.FIELD_ORDER
        ['n1', 'n2', 'n3', 'nd', 'data1', 'data2']
        >>> bmv = BMV.from_bytes(b'\x11\x22\x77\x81\x82zyxw\x02AB\x04DEFG')
        >>> bmv.n1  #doctest: +ELLIPSIS
        17
        >>> bmv.n2
        34
        >>> bmv  #doctest: +ELLIPSIS
        BMV(n1=17, n2=34, n3=119, nd=nd_1_short__bs(short=33154, bs=b'zyxw'), data1=b'AB', data2=b'DEFG')
        >>> bmv.nd  #doctest: +ELLIPSIS
        nd_1_short__bs(short=33154, bs=b'zyxw')
        >>> bmv.nd.bs
        b'zyxw'
        >>> bytes(bmv.nd)
        b'zyxw'
        >>> bmv.data1
        b'AB'
        >>> bmv.data2
        b'DEFG'
        >>> bytes(bmv)
        b'\x11"w\x81\x82zyxw\x02AB\x04DEFG'
        >>> list(bmv.transcribe_flat())
        [b'\x11', b'"', b'w', b'\x81\x82zyxw', b'\x02', b'AB', b'\x04', b'DEFG']



  A convenience wrapper for `BinaryMultiStruct`
  for `struct_format`s with a single field.

  Parameters:
  * `class_name`: the class name for the generated class
  * `struct_format`: the struct format string, specifying a
    single struct field
  * `field_name`: optional field name for the value,
    default `'value'`

  Example:

      >>> UInt16BE = BinarySingleStruct('UInt16BE', '>H')
      >>> UInt16BE.__name__
      'UInt16BE'
      >>> UInt16BE.format
      '>H'
      >>> UInt16BE.struct   #doctest: +ELLIPSIS
      <_struct.Struct object at ...>
      >>> field = UInt16BE.from_bytes(bytes((2,3)))
      >>> field
      UInt16BE(value=515)
      >>> field.value
      515



  A representation of a single value as the attribute `.value`.

  Subclasses must implement:
  * `parse` or `parse_value`
  * `transcribe` or `transcribe_value`

  *Method `BinarySingleValue.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse an instance from `bfr`.

  Subclasses must implement this method or `parse_value`.

  *Method `BinarySingleValue.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr` based on this class.

  Subclasses must implement this method or `parse`.

  *Method `BinarySingleValue.parse_value_from_bytes(bs, offset=0, length=None, **kw)`*:
  Parse a value from the bytes `bs` based on this class.
  Return `(value,offset)`.

  *Method `BinarySingleValue.scan_values(bfr: cs.buffer.CornuCopyBuffer, **kw)`*:
  Scan `bfr`, yield values.

  *Method `BinarySingleValue.transcribe(self)`*:
  Transcribe this instance as bytes.

  Subclasses must implement this method or `transcribe_value`.

  *Method `BinarySingleValue.transcribe_value(value)`*:
  Transcribe `value` as bytes based on this class.

  Subclasses must implement this method or `transcribe`.

  *Method `BinarySingleValue.value_from_bytes(bs, **from_bytes_kw)`*:
  Decode an instance from `bs` using `.from_bytes`
  and return the `.value` attribute.
  Keyword arguments are passed to `cls.from_bytes`.



  A NUL terminated UTF-16 string.

  *Method `BinaryUTF16NUL.__init__(self, value: str, *, encoding: str)`*:
  pylint: disable=super-init-not-called

  *Method `BinaryUTF16NUL.parse(bfr: cs.buffer.CornuCopyBuffer, *, encoding: str)`*:
  Parse the encoding and value and construct an instance.

  *Method `BinaryUTF16NUL.parse_value(bfr: cs.buffer.CornuCopyBuffer, *, encoding: str) -> str`*:
  Read a NUL terminated UTF-16 string from `bfr`, return a `UTF16NULField`.
  The mandatory parameter `encoding` specifies the UTF16 encoding to use
  (`'utf_16_be'` or `'utf_16_le'`).

  *Method `BinaryUTF16NUL.transcribe(self)`*:
  Transcribe `self.value` in UTF-16 with a terminating NUL.

  *Method `BinaryUTF16NUL.transcribe_value(value: str, encoding='utf-16')`*:
  Transcribe `value` in UTF-16 with a terminating NUL.



  A NUL terminated UTF-8 string.

  *Method `BinaryUTF8NUL.parse_value(bfr: cs.buffer.CornuCopyBuffer) -> str`*:
  Read a NUL terminated UTF-8 string from `bfr`, return field.

  *Method `BinaryUTF8NUL.transcribe_value(s)`*:
  Transcribe the `value` in UTF-8 with a terminating NUL.



  A run length encoded data chunk, with the length encoded as a `BSUInt`.

  *Property `BSData.data`*:
  An alias for the `.value` attribute.

  *Property `BSData.data_offset`*:
  The length of the length indicator,
  useful for computing the location of the raw data.

  *Method `BSData.data_offset_for(bs) -> int`*:
  Compute the `data_offset` which would obtain for the bytes `bs`.

  *Method `BSData.parse_value(bfr: cs.buffer.CornuCopyBuffer) -> bytes`*:
  Parse the data from `bfr`.

  *Method `BSData.transcribe_value(data)`*:
  Transcribe the payload length and then the payload.



  A float transcribed as a `BSString` of `str(float)`.

  *Method `BSSFloat.parse_value(bfr: cs.buffer.CornuCopyBuffer) -> float`*:
  Parse a `BSSFloat` from a buffer and return the `float`.

  *Method `BSSFloat.transcribe_value(f)`*:
  Transcribe a `float`.



  A run length encoded string, with the length encoded as a BSUInt.

  *Method `BSString.parse_value(bfr: cs.buffer.CornuCopyBuffer, encoding='utf-8', errors='strict') -> str`*:
  Parse a run length encoded string from `bfr`.

  *Method `BSString.transcribe_value(value: str, encoding='utf-8')`*:
  Transcribe a string.



  A binary serialised unsigned `int`.

  This uses a big endian byte encoding where continuation octets
  have their high bit set. The bits contributing to the value
  are in the low order 7 bits.

  *Method `BSUInt.decode_bytes(data, offset=0) -> Tuple[int, int]`*:
  Decode an extensible byte serialised unsigned `int` from `data` at `offset`.
  Return value and new offset.

  Continuation octets have their high bit set.
  The octets are big-endian.

  If you just have a `bytes` instance, this is the go. If you're
  reading from a stream you're better off with `parse` or `parse_value`.

  Examples:

      >>> BSUInt.decode_bytes(b'\0')
      (0, 1)

  Note: there is of course the usual `AbstractBinary.parse_bytes`
  but that constructs a buffer to obtain the individual bytes;
  this static method will be more performant
  if all you are doing is reading this serialisation
  and do not already have a buffer.

  *Method `BSUInt.parse_value(bfr: cs.buffer.CornuCopyBuffer) -> int`*:
  Parse an extensible byte serialised unsigned `int` from a buffer.

  Continuation octets have their high bit set.
  The value is big-endian.

  This is the go for reading from a stream. If you already have
  a bare bytes instance then the `.decode_bytes` static method
  is probably most efficient;
  there is of course the usual `AbstractBinary.parse_bytes`
  but that constructs a buffer to obtain the individual bytes.

  *Method `BSUInt.transcribe_value(n)`*:
  Encode an unsigned int as an entensible byte serialised octet
  sequence for decode. Return the bytes object.



  Flatten `chunks` into an iterable of `bytes`-like instances.
  None of the `bytes` instances will be empty.

  This exists to allow subclass methods to easily return
  transcribeable things (having a `.transcribe` method), ASCII
  strings or bytes or iterables or even `None`, in turn allowing
  them simply to return their superclass' chunks iterators
  directly instead of having to unpack them.

  An example from the `cs.iso14496.METABoxBody` class:

      def transcribe(self):
          yield super().transcribe()
          yield self.theHandler
          yield self.boxes

  The binary classes `flatten` the result of the `.transcribe`
  method to obtain `bytes` insteances for the object's bnary
  transcription.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'>d'` and presents the attributes ('value',).

  *Method `Float64BE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `Float64BE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `Float64BE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `Float64BE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'<d'` and presents the attributes ('value',).

  *Method `Float64LE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `Float64LE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `Float64LE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `Float64LE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'>h'` and presents the attributes ('value',).

  *Method `Int16BE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `Int16BE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `Int16BE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `Int16BE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'<h'` and presents the attributes ('value',).

  *Method `Int16LE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `Int16LE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `Int16LE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `Int16LE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'>l'` and presents the attributes ('value',).

  *Method `Int32BE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `Int32BE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `Int32BE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `Int32BE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'<l'` and presents the attributes ('value',).

  *Method `Int32LE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `Int32LE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `Int32LE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `Int32LE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  Convert a parse/transcribe specification `pt`
  into an `AbstractBinary` subclass.

  This is largely used to provide flexibility
  in the specifications for the `BinaryMultiValue` factory
  but can be used as a factory for other simple classes.

  If the specification `pt` is a subclass of `AbstractBinary`
  this is returned directly.

  If `pt` is a 2-tuple of `str`
  the values are presumed to be a format string for `struct.struct`
  and filed names separated by spaces;
  a new `BinaryMultiStruct` class is created from these and returned.

  Otherwise two functions
  `f_parse_value(bfr)` and `f_transcribe_value(value)`
  are obtained and used to construct a new `BinarySingleValue` class
  as follows:

  If `pt` has `.parse_value` and `.transcribe_value` callable attributes,
  use those for `f_parse_value` and `f_transcribe_value` respectively.

  Otherwise, if `pt` is an `int`
  define `f_parse_value` to obtain exactly that many bytes from a buffer
  and `f_transcribe_value` to return those bytes directly.

  Otherwise presume `pt` is a 2-tuple of `(f_parse_value,f_transcribe_value)`.



  Abstract binary class based on a `SimpleNamespace`,
  thus providing a nice `__str__` and a keyword based `__init__`.
  Implementors must still define `.parse` and `.transcribe`.

  To constrain the arguments passed to `__init__`,
  define an `__init__` which accepts specific keyword arguments
  and pass through to `super().__init__()`. Example:

      def __init__(self, *, field1=None, field2):
          """ Accept only `field1` (optional)
              and `field2` (mandatory).
          """
          super().__init__(field1=field1, field2=field2)



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'>H'` and presents the attributes ('value',).

  *Method `UInt16BE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `UInt16BE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `UInt16BE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `UInt16BE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'<H'` and presents the attributes ('value',).

  *Method `UInt16LE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `UInt16LE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `UInt16LE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `UInt16LE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'>L'` and presents the attributes ('value',).

  *Method `UInt32BE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `UInt32BE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `UInt32BE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `UInt32BE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'<L'` and presents the attributes ('value',).

  *Method `UInt32LE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `UInt32LE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `UInt32LE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `UInt32LE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'>Q'` and presents the attributes ('value',).

  *Method `UInt64BE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `UInt64BE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `UInt64BE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `UInt64BE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'<Q'` and presents the attributes ('value',).

  *Method `UInt64LE.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `UInt64LE.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `UInt64LE.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `UInt64LE.transcribe_value(value)`*:
  Transcribe a value back into bytes.



  An `AbstractBinary` `namedtuple` which parses and transcribes
  the struct format `'B'` and presents the attributes ('value',).

  *Method `UInt8.parse(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse from `bfr` via `struct.unpack`.

  *Method `UInt8.parse_value(bfr: cs.buffer.CornuCopyBuffer)`*:
  Parse a value from `bfr`, return the value.

  *Method `UInt8.transcribe(self)`*:
  Transcribe via `struct.pack`.

  *Method `UInt8.transcribe_value(value)`*:
  Transcribe a value back into bytes.





  *Release 20240630*:
  * flatten: do not yield empty str-as-ascii-bytes - we want to ensure that flatten never yields an empty bytes instance.
  * New AbstractBinary.write(f) method to write the binary form of this object to a file.
  * New BinarySingleValue.value_from_bytes(bytes) class method to return the value from a bytes instance.
  * Drop BinaryMixin, now folded directly into AbstractBinary.
  * AbstractBinary.scan: pass extra keyword arguments to AbstractBinary.parse, supporting plumbing eg a logging parameter through.

  *Release 20240422*:
  New _BinaryMultiValue_Base.for_json() method returning a dict containing the fields.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240201*:
  BREAKING CHANGE: drop the long deprecated PacketField related classes.

  *Release 20231129*:
  BinaryMultiStruct.parse: promote the buffer arguments to a CornuCopyBuffer.

  *Release 20230401*:
  * BinaryMixin.scan: `bfr` parameter may be any object acceptable to CornuCopyBuffer.promote.
  * BinaryMixin.scan: accept new optional with_offsets parameter; deprecate scan_with_offsets and scan_fspathi in favour of scan.

  *Release 20230212*:
  * BinaryMixin: new load(file) and save(file) methods.
  * BinaryMixin.scan: promote the bfr argument.

  *Release 20221206*:
  Documentation fix.

  *Release 20220605*:
  BinaryMixin: replace scan_file with scan_fspath, as the former left uncertainty about the amount of the file consumed.

  *Release 20210316*:
  * BSUInt: rename parse_bytes to decode_bytes, the former name conflicted with BinaryMixin.parse_bytes and broken the semantics.
  * Minor refactors.

  *Release 20210306*:
  MAJOR RELEASE: The PacketField classes and friends were hard to use; this release supplied a suite of easier to use and more consistent Binary* classes, and ports most of those things based on the old scheme to the new scheme.

  *Release 20200229*:
  * ListField: replace transcribe method with transcribe_value method, aids external use.
  * Add `.length` attribute to struct based packet classes providing the data length of the structure (struct.Struct.size).
  * Packet: new `add_deferred_field` method to consume the raw data for a field for parsing later (done automatically if the attribute is accessed).
  * New `@deferred_field` decorator for the parser for that stashed data.

  *Release 20191230.3*:
  Docstring tweak.

  *Release 20191230.2*:
  Documentation updates.

  *Release 20191230.1*:
  Docstring updates. Semantic changes were in the previous release.

  *Release 20191230*:
  * ListField: new __iter__ method.
  * Packet: __str__: accept optional `skip_fields` parameter to omit some field names.
  * Packet: new .add_from_value method to add a named field with a presupplied value.
  * Packet: new remove_field(field_name) and pop_field() methods to remove fields.
  * BytesesField: __iter__ yields the bytes values, transcribe=__iter__.
  * PacketField: propagate keyword arguments through various methods, required for parameterised PacketFields.
  * New UTF16NULField, a NUL terminated UTF16 string.
  * PacketField: provide a default `.transcribe_value` method which makes a new instance and calls its `.transcribe` method.
  * Documentation update and several minor changes.

  *Release 20190220*:
  * Packet.self_check: fields without a sanity check cause a warning, not a ValueError.
  * New Float64BE, Float64LE and BSSFloat classes for IEEE floats and floats-as-strings.
  * Additional module docstringage on subclassing Packet and PacketField.
  * BSString: drop redundant from_buffer class method.
  * PacketField.__init__: default to value=None if omitted.

  *Release 20181231*:
  flatten: do not yield zero length bytelike objects, can be misread as EOF on some streams.

  *Release 20181108*:
  * New PacketField.transcribe_value_flat convenience method to return a flat iterable of bytes-like objects.
  * New PacketField.parse_buffer generator method to parse instances of the PacketField from a buffer until end of input.
  * New PacketField.parse_buffer_values generator method to parse instances of the PacketField from a buffer and yield the `.value` attribute until end of input.

  *Release 20180823*:
  * Some bugfixes.
  * Define PacketField.__eq__.
  * BSUInt, BSData and BSString classes implementing the serialisations from cs.serialise.
  * New PacketField.value_from_bytes class method.
  * New PacketField.value_from_buffer method.

  *Release 20180810.2*:
  Documentation improvements.

  *Release 20180810.1*:
  Improve module description.

  *Release 20180810*:
  BytesesField.from_buffer: make use of the buffer's skipto method if discard_data is true.

  *Release 20180805*:
  * Packet: now an abstract class, new self_check method initially checking the
  * PACKET_FIELDS class attribute against the instance, new methods get_field
  * and set_field to fetch or replace existing fields, allow keyword arguments
  * to initialise the Packet fields and document the dependency on keyword
  * argument ordering.
  * PacketField: __len__ computed directory from a transcribe, drop other __len__
  * methods.
  * EmptyField singleton to use as a placeholder for missing optional fields.
  * BytesField: implement value_s and from_buffer.
  * multi_struct_field: implement __len__ for generated class.
  * flatten: treat memoryviews like bytes.
  * Assorted docstrings and fixes.

  *Release 20180801*:
  Initial PyPI release.


pkgId: 029b677851c4ab5d0d69cc74a3944c0a61807a41b5a3e991a51e83461310c90d
name: python-cs.buffer
arch: src
version: 20240630
epoch: 0
release: 1
url: https://pypi.org/project/cs.buffer/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.buffer-20240630-1.src.rpm
checksum_type: sha256
summary:
  Facilities to do with buffers, particularly CornuCopyBuffer, an automatically refilling buffer to support parsing of data streams.

description:
  Facilities to do with buffers, particularly CornuCopyBuffer,
  an automatically refilling buffer to support parsing of data streams.

  *Latest release 20240630*:
  * CornuCopyBuffer: new optional .final_offset attribute indicating the anticipated largest offset, automatcially set for regular files - aids progress bars etc.
  * Drop cs.py3, get pread from os.pread, require python>=3.3.
  * CornuCopyBuffer.close: set self.bufs = None to release memory and prevent accidental late use.
  * _FetchIterator: provide a mutex; SeekableIteratorMixin.seek, FDIterator.read,close: take the mutex.



  Wrapper for an iterator that copies every item retrieved to a callable.

  *Method `CopyingIterator.__init__(self, it, copy_to)`*:
  Initialise with the iterator `it` and the callable `copy_to`.



  An automatically refilling buffer intended to support parsing
  of data streams.

  Its purpose is to aid binary parsers
  which do not themselves need to handle sources specially;
  `CornuCopyBuffer`s are trivially made from `bytes`,
  iterables of `bytes` and file-like objects.
  See `cs.binary` for convenient parsing classes
  which work against `CornuCopyBuffer`s.

  Attributes:
  * `buf`: the first of any buffered leading chunks
    buffer of unparsed data from the input, available
    for direct inspection by parsers;
    normally however parsers will use `.extend` and `.take`.
  * `offset`: the logical offset of the buffer; this excludes
    buffered data and unconsumed input data

  *Note*: the initialiser may supply a cleanup function;
  although this will be called via the buffer's `.__del__` method
  a prudent user of a buffer should call the `.close()` method
  when finished with the buffer to ensure prompt cleanup.

  The primary methods supporting parsing of data streams are
  `.extend()` and `take()`.
  Calling `.extend(min_size)` arranges that the internal buffer
  contains at least `min_size` bytes.
  Calling `.take(size)` fetches exactly `size` bytes from the
  internal buffer and the input source if necessary and returns
  them, adjusting the internal buffer.

  len(`CornuCopyBuffer`) returns the length of any buffered data.

  bool(`CornuCopyBuffer`) tests whether len() > 0.

  Indexing a `CornuCopyBuffer` accesses the buffered data only,
  returning an individual byte's value (an `int`).

  A `CornuCopyBuffer` is also iterable, yielding data in whatever
  sizes come from its `input_data` source, preceeded by any
  content in the internal buffer.

  A `CornuCopyBuffer` also supports the file methods `.read`,
  `.tell` and `.seek` supporting drop in use of the buffer in
  many file contexts. Backward seeks are not supported. `.seek`
  will take advantage of the `input_data`'s .seek method if it
  has one, otherwise it will use consume the `input_data`
  as required.

  *Method `CornuCopyBuffer.__init__(self, input_data, buf=None, offset=0, seekable=None, copy_offsets=None, copy_chunks=None, close=None, progress=None, final_offset=None)`*:
  Prepare the buffer.

  Parameters:
  * `input_data`: an iterable of data chunks (`bytes`-like instances);
    if your data source is a file see the `.from_file` factory;
    if your data source is a file descriptor see the `.from_fd`
    factory.
  * `buf`: if not `None`, the initial state of the parse buffer
  * `offset`: logical offset of the start of the buffer, default `0`
  * `seekable`: whether `input_data` has a working `.seek` method;
    the default is `None` meaning that it will be attempted on
    the first skip or seek
  * `copy_offsets`: if not `None`, a callable for parsers to
    report pertinent offsets via the buffer's `.report_offset`
    method
  * `copy_chunks`: if not `None`, every fetched data chunk is
    copied to this callable

  The `input_data` is an iterable whose iterator may have
  some optional additional properties:
  * `seek`: if present, this is a seek method after the fashion
    of `file.seek`; the buffer's `seek`, `skip` and `skipto`
    methods will take advantage of this if available.
  * `offset`: the current byte offset of the iterator; this
    is used during the buffer initialisation to compute
    `input_data_displacement`, the difference between the
    buffer's logical offset and the input data iterable's logical offset;
    if unavailable during initialisation this is presumed to
    be `0`.
  * `end_offset`: the end offset of the iterator if known.
  * `close`: an optional callable
    that may be provided for resource cleanup
    when the user of the buffer calls its `.close()` method.
  * `progress`: an optional `cs.Progress.progress` instance
    to which to report data consumed from `input_data`;
    any object supporting `+=` is acceptable
  * `final_offset`: optional `int` specifying the largest
    offset expected to be reached, intended for uses such as
    callers presenting a pregress indication; this is, for
    example, provided by `CornuCopyBuffer.from_fd` for regular
    files using the `stat.st_size` field

  *Method `CornuCopyBuffer.__del__(self)`*:
  Release resources when the object is deleted.

  *Method `CornuCopyBuffer.__getitem__(self, index)`*:
  Fetch from the internal buffer.
  This does not consume data from the internal buffer.
  Note that this is an expensive way to access the buffer,
  particularly if `index` is a slice.

  If `index` is a `slice`, slice the join of the internal subbuffers.
  This is quite expensive
  and it is probably better to `take` or `takev`
  some data from the buffer.

  Otherwise `index` should be an `int` and the corresponding
  buffered byte is returned.

  This is usually not a very useful method;
  its primary use case is to probe the buffer to make a parsing decision
  instead of taking a byte off and (possibly) pushing it back.

  *Method `CornuCopyBuffer.__len__(self)`*:
  The length is the length of the internal buffer: data available without a fetch.

  *Method `CornuCopyBuffer.__next__(self)`*:
  Fetch a data chunk from the buffer.

  *Method `CornuCopyBuffer.as_fd(self, maxlength=Ellipsis)`*:
  Create a pipe and dispatch a `Thread` to copy
  up to `maxlength` bytes from `bfr` into it.
  Return the file descriptor of the read end of the pipe.

  The default `maxlength` is `Ellipsis`, meaning to copy all data.

  Note that the thread preemptively consumes from the buffer.

  This is useful for passing buffer data to subprocesses.

  *Method `CornuCopyBuffer.at_eof(self)`*:
  Test whether the buffer is at end of input.

  *Warning*: this will fetch from the `input_data` if the buffer
  is empty and so it may block.

  *Method `CornuCopyBuffer.bounded(self, end_offset) -> 'CornuCopyBuffer'`*:
  Return a new `CornuCopyBuffer` operating on a bounded view
  of this buffer.

  This supports parsing of the buffer contents without risk
  of consuming past a certain point, such as the known end
  of a packet structure.

  Parameters:
  * `end_offset`: the ending offset of the new buffer.
    Note that this is an absolute offset, not a length.

  The new buffer starts with the same offset as `self` and
  use of the new buffer affects `self`. After a flush both
  buffers will again have the same offset and the data consumed
  via the new buffer will also have been consumed from `self`.

  Here is an example.
  * Make a buffer `bfr` with 9 bytes of data in 3 chunks.
  * Consume 2 bytes, advancing the offset to 2.
  * Make a new bounded buffer `subbfr` extending to offset
    5. Its inital offset is also 2.
  * Iterate over it, yielding the remaining single byte chunk
    from ``b'abc'`` and then the first 2 bytes of ``b'def'``.
    The new buffer's offset is now 5.
  * Try to take 2 more bytes from the new buffer - this fails.
  * Flush the new buffer, synchronising with the original.
    The original's offset is now also 5.
  * Take 2 bytes from the original buffer, which succeeds.

  Example:

      >>> bfr = CornuCopyBuffer([b'abc', b'def', b'ghi'])
      >>> bfr.offset
      0
      >>> bfr.take(2)
      b'ab'
      >>> bfr.offset
      2
      >>> subbfr = bfr.bounded(5)
      >>> subbfr.offset
      2
      >>> for bs in subbfr:
      ...   print(bs)
      ...
      b'c'
      b'de'
      >>> subbfr.offset
      5
      >>> subbfr.take(2)
      Traceback (most recent call last):
          ...
      EOFError: insufficient input data, wanted 2 bytes but only found 0
      >>> subbfr.flush()
      >>> bfr.offset
      5
      >>> bfr.take(2)
      b'fg'

  *WARNING*: if the bounded buffer is not completely consumed
  then it is critical to call the new `CornuCopyBuffer`'s `.flush`
  method to push any unconsumed buffer back into this buffer.
  Recommended practice is to always call `.flush` when finished
  with the new buffer.
  The `CornuCopyBuffer.subbuffer` method returns a context manager
  which does this automatically.

  Also, because the new buffer may buffer some of the unconsumed
  data from this buffer, use of the original buffer should
  be suspended.

  *Property `CornuCopyBuffer.buf`*:
  The first buffer, or `b''` if nothing is buffered.

  *Method `CornuCopyBuffer.byte0(self)`*:
  Consume the leading byte and return it as an `int` (`0`..`255`).

  *Method `CornuCopyBuffer.close(self)`*:
  Close the buffer.
  This discards the internal buffer of "read but not consumed" data
  and calls the `close` callable supplied when the buffer was
  initialised, if any.

  *Property `CornuCopyBuffer.end_offset`*:
  Return the end offset of the input data (in buffer ordinates)
  if known, otherwise `None`.

  Note that this depends on the computation of the
  `input_offset_displacement` which takes place at the buffer
  initialisation, which in turn relies on the `input_data.offset`
  attribute, which at initialisation is presumed to be 0 if missing.

  *Method `CornuCopyBuffer.extend(self, min_size, short_ok=False)`*:
  Extend the buffer to at least `min_size` bytes.

  If `min_size` is `Ellipsis`, extend the buffer to consume all the input.
  This should really only be used with bounded buffers
  in order to avoid unconstrained memory consumption.

  If there are insufficient data available then an `EOFError`
  will be raised unless `short_ok` is true (default `False`)
  in which case the updated buffer will be short.

  *Method `CornuCopyBuffer.from_bytes(bs, offset=0, length=None, **kw)`*:
  Return a `CornuCopyBuffer` fed from the supplied bytes `bs`
  starting at `offset` and ending after `length`.

  This is handy for callers parsing using buffers but handed bytes.

  Parameters:
  * `bs`: the bytes
  * `offset`: a starting position for the data; the input
    data will start this far into the bytes
  * `length`: the maximium number of bytes to use; the input
    data will be cropped this far past the starting point;
    default: the number of bytes in `bs` after `offset`
  Other keyword arguments are passed to the buffer constructor.

  *Method `CornuCopyBuffer.from_fd(fd, readsize=None, offset=None, final_offset=None, **kw)`*:
  Return a new `CornuCopyBuffer` attached to an open file descriptor.

  Internally this constructs a `SeekableFDIterator` for regular
  files or an `FDIterator` for other files, which provides the
  iteration that `CornuCopyBuffer` consumes, but also seek
  support if the underlying file descriptor is seekable.

  Parameters:
  * `fd`: the operating system file descriptor
  * `readsize`: an optional preferred read size
  * `offset`: a starting position for the data; the file
    descriptor will seek to this offset, and the buffer will
    start with this offset
  Other keyword arguments are passed to the buffer constructor.

  *Method `CornuCopyBuffer.from_file(f, readsize=None, offset=None, final_offset=None, **kw)`*:
  Return a new `CornuCopyBuffer` attached to an open file.

  Internally this constructs a `SeekableFileIterator`, which
  provides the iteration that `CornuCopyBuffer` consumes
  and also seek support if the underlying file is seekable.

  Parameters:
  * `f`: the file like object
  * `readsize`: an optional preferred read size
  * `offset`: a starting position for the data; the file
    will seek to this offset, and the buffer will start with this
    offset
  Other keyword arguments are passed to the buffer constructor.

  *Method `CornuCopyBuffer.from_filename(filename: str, offset=None, final_offset=None, **kw)`*:
  Open the file named `filename` and return a new `CornuCopyBuffer`.

  If `offset` is provided, skip to that position in the file.
  A negative offset skips to a position that far from the end of the file
  as determined by its `Stat.st_size`.

  Other keyword arguments are passed to the buffer constructor.

  *Method `CornuCopyBuffer.from_mmap(fd, readsize=None, offset=None, **kw)`*:
  Return a new `CornuCopyBuffer` attached to an mmap of an open
  file descriptor.

  Internally this constructs a `SeekableMMapIterator`, which
  provides the iteration that `CornuCopyBuffer` consumes, but
  also seek support.

  Parameters:
  * `fd`: the operating system file descriptor
  * `readsize`: an optional preferred read size
  * `offset`: a starting position for the data; the file
    descriptor will seek to this offset, and the buffer will
    start with this offset
  Other keyword arguments are passed to the buffer constructor.

  *Method `CornuCopyBuffer.hint(self, size)`*:
  Hint that the caller is seeking at least `size` bytes.

  If the `input_data` iterator has a `hint` method, this is
  passed to it.

  *Method `CornuCopyBuffer.iter(self, maxlength)`*:
  Yield chunks from the buffer
  up to `maxlength` in total
  or until EOF if `maxlength` is `Ellipsis`.

  *Method `CornuCopyBuffer.next(self)`*:
  Fetch a data chunk from the buffer.

  *Method `CornuCopyBuffer.peek(self, size, short_ok=False)`*:
  Examine the leading bytes of the buffer without consuming them,
  a `take` followed by a `push`.
  Returns the bytes.

  *Method `CornuCopyBuffer.promote(obj)`*:
  Promote `obj` to a `CornuCopyBuffer`,
  used by the @cs.deco.promote` decorator.

  Promotes:
  * `int`: assumed to be a file descriptor of a file open for binary read
  * `str`: assumed to be a filesystem pathname
  * `bytes` and `bytes`like objects: data
  * has a `.read1` or `.read` method: assume a file open for binary read
  * iterable: assumed to be an iterable of `bytes`like objects

  *Method `CornuCopyBuffer.push(self, bs)`*:
  Push the chunk `bs` onto the front of the buffered data.
  Rewinds the logical `.offset` by the length of `bs`.

  *Method `CornuCopyBuffer.read(self, size, one_fetch=False)`*:
  Compatibility method to allow using the buffer like a file.

  Parameters:
  * `size`: the desired data size
  * `one_fetch`: do a single data fetch, default `False`

  In `one_fetch` mode the read behaves like a POSIX file read,
  returning up to to `size` bytes from a single I/O operation.

  *Method `CornuCopyBuffer.read1(self, size)`*:
  Shorthand method for `self.read(size,one_fetch=True)`.

  *Method `CornuCopyBuffer.readline(self)`*:
  Return a binary "line" from `self`, where a line is defined by
  its ending `b'\n'` delimiter.
  The final line from a buffer might not have a trailing newline;
  `b''` is returned at EOF.

  Example:

      >>> bfr = CornuCopyBuffer([b'abc', b'def\nhij'])
      >>> bfr.readline()
      b'abcdef\n'
      >>> bfr.readline()
      b'hij'
      >>> bfr.readline()
      b''
      >>> bfr.readline()
      b''

  *Method `CornuCopyBuffer.report_offset(self, offset)`*:
  Report a pertinent offset.

  *Method `CornuCopyBuffer.seek(self, offset, whence=None, short_ok=False)`*:
  Compatibility method to allow using the buffer like a file.
  This returns the resulting absolute offset.

  Parameters are as for `io.seek` except as noted below:
  * `whence`: (default `os.SEEK_SET`). This method only supports
    `os.SEEK_SET` and `os.SEEK_CUR`, and does not support seeking to a
    lower offset than the current buffer offset.
  * `short_ok`: (default `False`). If true, the seek may not reach
    the target if there are insufficent `input_data` - the
    position will be the end of the `input_data`, and the
    `input_data` will have been consumed; the caller must check
    the returned offset to check that it is as expected. If
    false, a `ValueError` will be raised; however, note that the
    `input_data` will still have been consumed.

  *Method `CornuCopyBuffer.selfcheck(self, msg='')`*:
  Integrity check for the buffer, useful during debugging.

  *Method `CornuCopyBuffer.skip(self, toskip, copy_skip=None, short_ok=False)`*:
  Advance position by `skip_to`. Return the new offset.

  Parameters:
  * `toskip`: the distance to advance
  * `copy_skip`: callable to receive skipped data.
  * `short_ok`: default `False`; if true then skip may return before
    `skipto` bytes if there are insufficient `input_data`.

  *Method `CornuCopyBuffer.skipto(self, new_offset, copy_skip=None, short_ok=False)`*:
  Advance to position `new_offset`. Return the new offset.

  Parameters:
  * `new_offset`: the target offset.
  * `copy_skip`: callable to receive skipped data.
  * `short_ok`: default `False`; if true then skipto may return before
    `new_offset` if there are insufficient `input_data`.

  Return values:
  * `buf`: the new state of `buf`
  * `offset`: the final offset; this may be short if `short_ok`.

  *Method `CornuCopyBuffer.subbuffer(self, end_offset)`*:
  Context manager wrapper for `.bounded`
  which calls the `.flush` method automatically
  on exiting the context.

  Example:

      
      with bfr.subbuffer(bfr.offset+128) as subbfr:
          id3v1 = ID3V1Frame.parse(subbfr)
          
          assert subbfr.at_eof()

  *Method `CornuCopyBuffer.tail_extend(self, size)`*:
  Extend method for parsers reading "tail"-like chunk streams,
  typically raw reads from a growing file.

  This may read 0 bytes at EOF, but a future read may read
  more bytes if the file grows.
  Such an iterator can be obtained from
  ``cs.fileutils.read_from(..,tail_mode=True)``.

  *Method `CornuCopyBuffer.take(self, size, short_ok=False)`*:
  Return the next `size` bytes.
  Other arguments are as for `.extend()`.

  This is a thin wrapper for the `.takev` method.

  *Method `CornuCopyBuffer.takev(self, size, short_ok=False)`*:
  Return the next `size` bytes as a list of chunks
  (because the internal buffering is also a list of chunks).
  Other arguments are as for `.extend()`.

  See `.take()` to get a flat chunk instead of a list.

  *Method `CornuCopyBuffer.tell(self)`*:
  Compatibility method to allow using the buffer like a file.



  An iterator over the data of a file descriptor.

  *Method `FDIterator.__init__(self, fd: int, offset=None, readsize=None, align=True)`*:
  Initialise the iterator.

  Parameters:
  * `fd`: file descriptor
  * `offset`: the initial logical offset, kept up to date by
    iteration; the default is the current file position.
  * `readsize`: a preferred read size; if omitted then
    `DEFAULT_READSIZE` will be stored
  * `align`: whether to align reads by default: if true then
    the iterator will do a short read to bring the `offset`
    into alignment with `readsize`; the default is `True`

  *Method `FDIterator.close(self)`*:
  Close `self.fd` if it is nt yet `None`.



  An iterator over the data of a file object.

  *Method `FileIterator.__init__(self, fp, offset=None, readsize=None, align=False)`*:
  Initialise the iterator.

  Parameters:
  * `fp`: file object
  * `offset`: the initial logical offset, kept up to date by
    iteration; the default is 0.
  * `readsize`: a preferred read size; if omitted then
    `DEFAULT_READSIZE` will be stored
  * `align`: whether to align reads by default: if true then
    the iterator will do a short read to bring the `offset`
    into alignment with `readsize`; the default is `False`

  *Method `FileIterator.close(self)`*:
  Detach from the file. Does *not* call `fp.close()`.



  An iterator over the data of a seekable file descriptor.

  *Property `SeekableFDIterator.end_offset`*:
  The end offset of the file.



  An iterator over the data of a seekable file object.

  *Note*: the iterator closes the file on __del__ or if its
  .close method is called.

  *Method `SeekableFileIterator.__init__(self, fp, offset=None, **kw)`*:
  Initialise the iterator.

  Parameters:
  * `fp`: file object
  * `offset`: the initial logical offset, kept up to date by
    iteration; the default is the current file position.
  * `readsize`: a preferred read size; if omitted then
    `DEFAULT_READSIZE` will be stored
  * `align`: whether to align reads by default: if true then
    the iterator will do a short read to bring the `offset`
    into alignment with `readsize`; the default is `False`

  *Method `SeekableFileIterator.seek(self, new_offset, mode=0)`*:
  Move the logical file pointer.

  WARNING: moves the underlying file's pointer.



  Mixin supplying a logical with a `seek` method.

  *Method `SeekableIteratorMixin.seek(self, new_offset, mode=0)`*:
  Move the logical offset.



  An iterator over the data of a mappable file descriptor.

  *Method `SeekableMMapIterator.__init__(self, fd: int, offset=None, readsize=None, align=True)`*:
  Initialise the iterator.

  Parameters:
  * `offset`: the initial logical offset, kept up to date by
    iteration; the default is the current file position.
  * `readsize`: a preferred read size; if omitted then
    `DEFAULT_READSIZE` will be stored
  * `align`: whether to align reads by default: if true then
    the iterator will do a short read to bring the `offset`
    into alignment with `readsize`; the default is `True`

  *Method `SeekableMMapIterator.close(self)`*:
  Close the mmap and detach.

  *Property `SeekableMMapIterator.end_offset`*:
  The end offset of the mmap memoryview.





  *Release 20240630*:
  * CornuCopyBuffer: new optional .final_offset attribute indicating the anticipated largest offset, automatcially set for regular files - aids progress bars etc.
  * Drop cs.py3, get pread from os.pread, require python>=3.3.
  * CornuCopyBuffer.close: set self.bufs = None to release memory and prevent accidental late use.
  * _FetchIterator: provide a mutex; SeekableIteratorMixin.seek, FDIterator.read,close: take the mutex.

  *Release 20240412*:
  CornuCopyBuffer.buf: return b"" if nothing is buffered.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240201*:
  CornuCopyBuffer: read1() method shorthand for read(..,one_fetch=True).

  *Release 20230401*:
  * CornuCopyBuffer.promote: document accepting an iterable as an iterable of bytes.
  * CornuCopyBuffer: new readline() method to return a binary line from the buffer.
  * CornuCopyBuffer.promote: assume objects with a .read1 or .read are files.

  *Release 20230212.2*:
  * BREAKING: drop @chunky, superceded by @promote for CornuCopyBuffer parameters.
  * CornuCopyBuffer: subclass Promotable.

  *Release 20230212.1*:
  Add missing requirement for cs.gimmicks.

  *Release 20230212*:
  CornuCopyBuffer: new promote() method to promote int,str,bytes, also assumes nonspecific iterables yield byteslike instances.

  *Release 20211208*:
  CornuCopyBuffer.__init__: bugfix for self.input_data when copy_chunks is not None.

  *Release 20210316*:
  * New CornuCopyBuffer.from_filename factory method.
  * New CornuCopyBuffer.peek method to examine the leading bytes from the buffer.

  *Release 20210306*:
  * CornuCopyBuffer.from_file: improve the seekability test, handle files with no .tell().
  * CornuCopyBuffer.from_bytes: bugfix: set the buffer.offset to the supplied offset.
  * CornuCopyBuffer.from_bytes: queue a memoryview of the supplied bytes.

  *Release 20201102*:
  CornuCopyBuffer: new optional `progress` parameter for reporting data consumed from `input_data`.

  *Release 20201021*:
  * CornuCopyBuffer.from_file: changes to the test for a .seek method.
  * CornuCopyBuffer.read: call extend with short_ok=True.
  * CornuCopyBuffer.from_fd: record the fd as .fd, lets users os.fstat(bfr.fd).
  * New CornuCopyBuffer.as_fd method to return a readable file descriptor fed from the buffer by a Thread, intended for feeding subprocesses.
  * New CornuCopyBuffer.iter(maxlength) to return an iterator of up to maxlength bytes.
  * CornuCopyBuffer.__init__: new "close" parameter to release resources; new CornuCopyBuffer.close method to call this.
  * Some small fixes.

  *Release 20200517*:
  * CornuCopyBuffer.skip: bugfix sanity check.
  * FileIterator: do not close the supplied file, just set self.fp=None.
  * Improve EOFError message text.

  *Release 20200328*:
  * CornuCopyBuffer.takev: bugfix adjustment of buf.offset, was not always done.
  * CornuCopyBuffer.__getitem__: add slice support, note how expensive it is to use.

  *Release 20200229*:
  * New CornuCopyBuffer.byte0() method consuming the next byte and returning it as an int.
  * CornuCopyBuffer.takev: bugfix for size=0, logic refactor.
  * CornuCopyBuffer: new .selfcheck method.

  *Release 20200130*:
  CornuCopyBuffer.skip: bugfix adjustment of skipto for already buffered data.

  *Release 20191230.1*:
  Docstring updates. Semantic changes were in the previous release.

  *Release 20191230*:
  * CornuCopyBuffer: accept a size of Ellipsis in .take and .extend methods, indicating "all the remaining data".
  * CornuCopyBuffer: refactor the buffering, replacing .buf with .bufs as an array of chunks;
  * this enables support for the new .push method and reduces memory copying.

  *Release 20181231*:
  Small bugfix.

  *Release 20181108*:
  New at_eof() method. Python 2 tweak to support incidental import by python 2 even if unused.

  *Release 20180823*:
  Better handling of seekable and unseekable input data. Tiny bugfix for from_bytes sanity check.

  *Release 20180810*:
  * Refactor SeekableFDIterator and SeekableFileIterator to subclass new SeekableIterator.
  * New SeekableMMapIterator to process a memory mapped file descriptor, intended for large files.
  * New CornuCopyBuffer.hint method to pass a length hint through to the input_data iterator
  * if it has a `hint` method, causing it possibly to make a differently sized fetch.
  * SeekableIterator: new __del__ method calling self.close() - subclasses must provide
  * a .close, which should be safe to call multiple times.
  * CornuCopyBuffer: add support for .offset and .end_offset optional attributes on the input_data iterator.
  * _BoundedBufferIterator: add .offset property plumbed to the underlying buffer offset.
  * New CornuCopyBuffer.from_mmap to make a mmap backed buffer so that large data can be returned without penalty.
  * Assorted fixes and doc improvements.

  *Release 20180805*:
  Bugfixes for at_eof method and end_offset initialisation.

  *Release 20180726.1*:
  Improve docstrings and release with better long_description.

  *Release 20180726*:
  First PyPI release: CornuCopyBuffer and friends.


pkgId: 351649832b237261f698f9bee3873684397374a99bf93d3af19f04827a7cd54d
name: python-cs.cmdutils
arch: src
version: 20241117
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-20241117-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 20241117*:
  * _COMMON_OPT_SPECS: include a --dry-run option, alter for -n.
  * SubCommand.usage_text: one line listing for common subcommands.



  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] [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 even if no subcommand-names provided.
          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.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] [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 even if no subcommand-names provided.

  *`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 0x1052cf7f0>, **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 0x1052cf0a0>)`*:
  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 0x1052cf2e0>, **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.

  Parameters:
  * `subcmd`: the subcommand name
  * `short`: just include the first line of the usage message,
    intented for when there are many subcommands

  *`BaseCommand.subcommands`*

  *`BaseCommand.usage_text(self, *, cmd=None, short=False, show_common=False, show_subcmds=None)`*:
  Compute the "Usage:" message for this class
  from the top level `USAGE_FORMAT`
  and the `'Usage:'`-containing docstrings of its `cmd_*` methods.

  Parameters:
  * `cmd`: optional command name, default derived from the subcommand
  * `short`: default `False`; if true then just provide the opening sentence
  * `show_common`: show the `COMMON_OPT_SPECS` usage, default `False`
  * `show_subcmds`: constrain the usage to particular subcommands
    named in `show_subcmds`; this is used to produce a shorter
    usage for subcommand usage failures



  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.default_usage(self)`*:
  Return `'{cmd} [options...]'` or `'{cmd} subcommand [options...]'`.

  *`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.

  This first tries `self.method.USAGE_FORMAT`, falling back
  to deriving it from `obj_docstring(self.method)`.
  Usually a subcommand which is another `BaseCommand` instance
  will have a `.USAGE_FORMAT` attribute and a subcommand which
  is a method will derive the usage from its docstring.

  When deriving from the docstring we look for a paragraph
  commencing with the string `Usage:` and otherwise fall back
  to its first parapgraph.

  *`SubCommand.get_usage_keywords(self)`*:
  Return a mapping to be used when formatting the usage format string.

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

  *`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 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: c8a30b5c03481fa5fa183abb4277c7364535450b7e3017ce3ef966cc6bac393a
name: python-cs.dateutils
arch: src
version: 20230210
epoch: 0
release: 1
summary: A few conveniences to do with dates and times.
url: https://bitbucket.org/cameron_simpson/css/commits/all
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.dateutils-20230210-1.src.rpm
checksum_type: sha256
description:
  A few conveniences to do with dates and times.

  *Latest release 20230210*:
  * Drop Python 2 support.
  * Make timezones mandatory where previously they were assumed.

  There are some other PyPI modules providing richer date handling
  than the stdlib `datetime` module.
  This module mostly contains conveniences used in my other code;
  you're welcome to it, but it does not pretend to be large or complete.



  Convert a timezone aware `datetime` to a UNIX timestamp.



  Return a date in ISO8601 YYYY-MM-DD format, or YYYYMMDD if not `dashed`.

  Modern Pythons have a `datetime.isoformat` method, you use that.



  Convert a localtime `date` into a UNIX timestamp.



  tzinfo class based on +HHMM / -HHMM strings.



  Convert a a UNIX timestamp to a `datetime` in the timezone `tz`.



  A mixin for classes with a `.unixtime` attribute,
  a `float` storing a UNIX timestamp.





  *Release 20230210*:
  * Drop Python 2 support.
  * Make timezones mandatory where previously they were assumed.

  *Release 20210306*:
  Initial release, used by cs.sqltags.


pkgId: ef4c24d39bb1bb5beed23e211710f47bf2dd5c37a62423b37d74a573d48550c7
name: python-cs.deco
arch: src
version: 20241007
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-20241007-1.src.rpm
checksum_type: sha256
description:
  Assorted function decorators.

  *Latest release 20241007*:
  @uses_cmd_option: bugfix the infill of the defaults.



  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)`*:
  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)` 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)



  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`.

  Example:

      @uses_cmd_option(quiet=False)
      def func(x, *, quiet, **kw):
          if not quiet:
              print("something", x, kw)
          ... 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)`.





  *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: 9c3d4e643978185f85e5aff392246ce535d351dcfae0b4561ed8990c794f8dae
name: python-cs.deco
arch: src
version: 20241109
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-20241109-1.src.rpm
checksum_type: sha256
description:
  Assorted function decorators.

  *Latest 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.



  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 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: 9addd74c6e56e3b65545b30456d08aa8345184b70401b8499ac8aa6ac86c405a
name: python-cs.excutils
arch: src
version: 20240630
epoch: 0
release: 1
summary: Convenience facilities for managing exceptions.
url: https://pypi.org/project/cs.excutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.excutils-20240630-1.src.rpm
checksum_type: sha256
description:
  Convenience facilities for managing exceptions.

  *Latest release 20240630*:
  * LogExceptions: new optinal `log` param for the lgging call.
  * @logexc: use @decorator like other civilised decorators, pass decorator params to LogExceptions.



  Decorator to catch specific exception types and return a defined default value.



  Decorator to log exceptions and reraise.



  Decorator to log exceptions and reraise for generators.



  Wrapper for `NoExceptions` which reports exceptions and optionally
  suppresses them.



  Decorator to wrap a function which should never raise an exception.
  Instead, any raised exception is attempted to be logged.

  A significant side effect is of course that if the function raises an
  exception it now returns `None`.
  My primary use case is actually to wrap logging functions,
  which I have had abort otherwise sensible code.



  Decorator to wrap a generator which should never raise an exception.
  Instead, any raised exception is attempted to be logged and iteration ends.

  My primary use case is wrapping generators chained in a pipeline,
  as in cs.later.Later.pipeline.



  A context manager to catch _all_ exceptions and log them.

  Arguably this should be a bare try...except but that's syntacticly
  noisy and separates the catch from the top.
  For simple function calls `return_exc_info()` is probably better.

  *Method `NoExceptions.__init__(self, handler)`*:
  Initialise the `NoExceptions` context manager.

  The `handler` is a callable which
  expects `(exc_type,exc_value,traceback)`
  and returns `True` or `False`
  for the `__exit__` method of the manager.
  If `handler` is `None`, the `__exit__` method
  always returns `True`, suppressing any exception.



  Run the supplied function and arguments.
  Return `(func_return, None)`
  in the case of successful operation
  and `(None, exc_info)` in the case of an exception.

  `exc_info` is a 3-tuple of `(exc_type, exc_value, exc_traceback)`
  as returned by `sys.exc_info()`.
  If you need to protect a whole suite and would rather not move it
  into its own function, consider the NoExceptions context manager.



  Decorator function to wrap functions whose exceptions should be caught,
  such as inside event loops or worker threads.

  It causes a function to return `(func_return, None)`
  in the case of successful operation
  and `(None, exc_info)` in the case of an exception.

  `exc_info` is a 3-tuple of `(exc_type, exc_value, exc_traceback)`
  as returned by `sys.exc_info()`.



  Substitute for @property which lets AttributeErrors escape as RuntimeErrors.



  Decorator to transmute an inner exception to another exception type.

  The motivating use case is properties in a class with a
  `__getattr__` method;
  if some inner operation of the property function raises `AttributeError`
  then the property is bypassed in favour of `__getattr__`.
  Confusion ensues.

  In principle this can be an issue with any exception raised
  from "deeper" in the call chain, which can be mistaken for a
  "shallow" exception raised by the function itself.



  Decorator to transmute `AttributeError` into a `RuntimeError`.



  Decorator for stub methods that must be implemented by a stub class.





  *Release 20240630*:
  * LogExceptions: new optinal `log` param for the lgging call.
  * @logexc: use @decorator like other civilised decorators, pass decorator params to LogExceptions.

  *Release 20230212.1*:
  Fix imports.

  *Release 20230212*:
  Import cs.gimmicks instead of cs.logutils, reduces dependencies.

  *Release 20221228*:
  Get warning etc from cs.gimmicks, breaks circular import with cs.logutils.

  *Release 20221207*:
  @unattributable: bugfix decorator construction to accomodate callable() @decorator precheck.

  *Release 20210123*:
  @transmute: refactor to raise chained exceptions in Python 3+.

  *Release 20190812*:
  LogExceptions: drop stack trace noise.

  *Release 20190220*:
  New decorator @exc_fold to catch particular exceptions, log an error and return a defined value.

  *Release 20190101*:
  @logexc: handle missing func.__name__.

  *Release 20170904*:
  Minor updates, improved docstring.

  *Release 20160828*:
  * @unattributable and @safe_property decorators, used to protect properties from inner AttributeErrors.
  * Improved exception practices.

  *Release 20150118*:
  metadata updates

  *Release 20150110*:
  Initial distinfo for pypi release.


pkgId: 6940c0cdd80f5fa2c3df1f9422b1fa9e066c4f4e98293774c397e6569625fff7
name: python-cs.fileutils
arch: src
version: 20241007.1
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-20241007.1-1.src.rpm
checksum_type: sha256
description:
  My grab bag of convenience functions for files and filenames/pathnames.

  *Latest release 20241007.1*:
  Bugfix for @atomic_filename.



  Return the absolute path of `path` with respect to `from_file`,
  as one might do for an include file.



  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(**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 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: e6a9e6defce61292abf9fc80b13b3d7c7902cc26924be408f4c32edbc12d1cf1
name: python-cs.fs
arch: src
version: 20241007
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-20241007-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 20241007*:
  FSPathBasedSingleton.__init__: use an NRLock for the default lock, using a late import with fallback to Lock.



  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)



  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 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: b703f1bf182829cbbbb372b74483ffefbaac03293a24f0f02e079ba679ab0cb0
name: python-cs.fsm
arch: src
version: 20240721.1
epoch: 0
release: 1
summary: Basic Finite State Machine (FSM) tools.
url: https://pypi.org/project/cs.fsm/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.fsm-20240721.1-1.src.rpm
checksum_type: sha256
description:
  Basic Finite State Machine (FSM) tools.

  *Latest release 20240721.1*:
  FSM.fsm_print_state_diagram: bugfix: plumb fmt through to gvprint.



  Subclass of `FSMError` Raised when trying to make use of an
  `FSM` which is cancelled.

  For example, this is raised by a `cs.result.Result`
  when accessing `.result` or `.exc_info` after cancellation.

  *`CancellationError.__init__(self, message=None, *, fsm=None, **kw)`*:
  Initialise the `CancellationError`.

  The optional `message` parameter (default `"cancelled"`)
  is set as the `message` attribute.
  Other keyword parameters set their matching attributes.



  Base class for a finite state machine (FSM).

  The allowed states and transitions are defined by the class
  attribute `FSM_TRANSITIONS`, a mapping of
  *state*->*event*->*new_state*.

  Each instance has the following attributes:
  * `fsm_state`: the current state value.
  * `fsm_history`: an optional iterable of `FSMTransitionEvent`
    state transitions recorded by the `fsm_event` method.
    Usually this would be `None` (the default) or a `list`.

  <figure>
      <svg width="8pt" height="8pt"
     viewBox="0.00 0.00 8.00 8.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 4)">
    <title>FSM State Diagram</title>
    <polygon fill="white" stroke="none" points="-4,4 -4,-4 4,-4 4,4 -4,4"/>
    </g>
    </svg>
    <figcaption>FSM State Diagram</figcaption>
  </figure>


  *`FSM.__init__(self, state=None, *, history=None, lock=None, transitions=None)`*:
  Initialise the `FSM` from:
  * `state`: optional _positional_ parameter for the initial state,
    default `self.FSM_DEFAULT_STATE` or the first key from `self.FSM_TRANSITIONS`
  * `history`: an optional object to record state transition
    history, default `None`; if not `None` this should be an
    iterable object with a `.append(entry)` method such as a
    `list`.
  * `lock`: an optional mutex to control access;
    if presupplied and shared with the caller
    it should probably be an `RLock`;
    the default is a `Lock`, which is enough for `FSM` private use
  * `transitions`: optional *state*->*event*->*state* mapping;
    if provided, this will override the class `FSM_TRANSITIONS` mapping

  Note that the `FSM` base class does not provide a
  `FSM_DEFAULT_STATE` attribute; a default `state` value of
  `None` will leave `.fsm_state` _unset_.

  This behaviour is is chosen mostly to support subclasses
  with unusual behaviour, particularly Django's `Model` class
  whose `refresh_from_db` method seems to not refresh fields
  which already exist, and setting `.fsm_state` from a
  `FSM_DEFAULT_STATE` class attribute thus breaks this method.
  Subclasses of this class and `Model` should _not_ provide a
  `FSM_DEFAULT_STATE` attribute, instead relying on the field
  definition to provide this default in the usual way.

  *`FSM.__getattr__(self, attr)`*:
  Provide the following attributes:
  - present the state names as attributes, for example:
    `self.PENDING=='PENDING'` if there is a `'PENDING'` state
  - present `is_`*statename* as a Boolean testing whether
    `self.fsm_state==`*statename*`.upper()`
  - a callable calling `self.fsm_event(attr)` if `attr`
    is an event name for the current state
  Fall back to the superclass `__getattr__`.

  *`FSM.dot_node_palette_key`*:
  Default palette index is `self.fsm_state`,
  overriding `DOTNodeMixin.dot_node_palette_key`.

  *`FSM.fsm_as_svg(self, layout=None, history_style=None, **dot_kw) -> str`*:
  Render the state transition diagram as SVG.

  *`FSM.fsm_callback(self, state, callback)`*:
  Register a callback to be called immediately on transition
  to `state` as `callback(self,FSMEventTransition)`.
  The special `state` value `FSM.FSM_ANY_STATE` may be supplied
  to register a callback which fires for every state transition.

      >>> fsm = FSM('state1',transitions={
      ...   'state1':{'ev_a':'state2'},
      ...   'state2':{'ev_b':'state1'},
      ... })
      >>> fsm.fsm_callback('state2',lambda task, transition: print(task, transition))
      >>> fsm.fsm_callback(FSM.FSM_ANY_STATE,lambda task, transition: print("ANY", task, transition))
      >>> fsm.ev_a(foo=3) # doctest: +ELLIPSIS
      ANY FSM:state2 FSMTransitionEvent(old_state='state1', new_state='state2', event='ev_a', when=..., extra={'foo': 3})
      FSM:state2 FSMTransitionEvent(old_state='state1', new_state='state2', event='ev_a', when=..., extra={'foo': 3})
      'state2'
      >>> fsm.ev_b(foo=4) # doctest: +ELLIPSIS
      ANY FSM:state1 FSMTransitionEvent(old_state='state2', new_state='state1', event='ev_b', when=..., extra={'foo': 4})
      'state1'

  *`FSM.fsm_callback_discard(self, state, callback)`*:
  Deregister a callback for `state`.

  *`FSM.fsm_dot`*:
  A DOT syntax description of the state diagram in the current state.

  *`FSM.fsm_event(self, event, **extra)`*:
  Transition the FSM from the current state to a new state based on `event`.
  Call any callbacks associated with the new state.
  Returns the new state.

  Optional information may be passed as keyword arguments.

  A `transition` instance of `FSMTransitionEvent` is created
  with the following attributes:
  * `old_state`: the state when `fsm_event` was called
  * `new_state`: the new state
  * `event`: the `event`
  * `when`: a UNIX timestamp from `time.time()`
  * `extra`: a `dict` with the `extra` information

  If `self.fsm_history` is not `None`,
  `transition` is appended to it.

  If there are callbacks for `new_state` or `FSM.FSM_ANY_STATE`,
  call each callback as `callback(self,transition)`.

  *Important note*: the callbacks are run in series in the
  current `Thread`.  If you need to dispatch a long running
  activity from a state transtion, the callback should still
  return promptly.

  *`FSM.fsm_event_is_allowed(self, event)`*:
  Test whether `event` is permitted in the current state.
  This can be handy as a pretest.

  *`FSM.fsm_events`*:
  Return a list of the events valid for the current state.

  *`FSM.fsm_history`*:
  History property wrapping private attribute.
  This aids subclassing where the history is not a local attribute.

  *`FSM.fsm_print(self, file=None, fmt=None, layout=None, **dot_kw)`*:
  Print the state transition diagram to `file`, default `sys.stdout`,
  in format `fmt` using the engine specified by `layout`, default `'dot'`.
  This is a wrapper for `cs.gvutils.gvprint`.

  *`FSM.fsm_print_state_diagram(file=None, *, fmt=None, graph_name=None, history=None, history_style=None, state=None, transitions=None, **gvprint_kw)`*:
  Print the state diagram via `cs.gvutils.gvprint`.

  The DOT syntax graph description is computed with
  `FSM.fsm_state_diagram_as_dot` and the `graph_name`,
  `history`, `history_style`, `state` and `transitions`
  parameters are passed through to this.

  If `fmt` is specified as `dot` then the DOT and any remaining
  keyword arguments are passed to `print()`.

  Otherwise any remaining keyword paramaeters are passed to `gvprint`.

  *`FSM.fsm_state_diagram_as_dot(transitions=None, *, sep='\n', state=None, graph_name=None, history=None, history_style=None) -> str`*:
  Compute a DOT syntax graph description of the state diagram.

  Parameters:
  * `transitions`: optional mapping of *state*->*event*->*state*,
    default `cls.FSM_TRANSITIONS`
  * `state`: optional current state name, a key of
  * `sep`: optional separator between "lines", default `'\n'`
  * `graph_name`: optional name for the graph, default the class name
  * `history`: optional event transition history
  * `history_style`: optional style mapping for event transition history,
    used to style edges which have been traversed

  *`FSM.fsm_svg`*:
  The state transition diagram as SVG.

  *`FSM.fsm_transitions_as_dot(self, transitions=None, **diagram_kw) -> str`*:
  Compute a DOT syntax graph description of the state diagram.

  Parameters:
  * `transitions`: optional mapping of *state*->*event*->*state*,
    default `self.FSM_TRANSITIONS`
  * `sep`: optional separator between "lines", default `'\n'`
  * `graph_name`: optional name for the graph, default the class name
  * `history_style`: optional style mapping for event transition history,
    used to style edges which have been traversed



  An exception associated with an `FSM`.

  These have a `.fsm` attribute storing an (optional) `FSM`
  reference supplied at initialisation.



  Type variable.

  The preferred way to construct a type variable is via the dedicated
  syntax for generic functions, classes, and type aliases::

      class Sequence[T]:  # T is a TypeVar
          ...

  This syntax can also be used to create bound and constrained type
  variables::

      
      class StrSequence[S: str]:
          ...

      
      class StrOrBytesSequence[A: (str, bytes)]:
          ...

  However, if desired, reusable type variables can also be constructed
  manually, like so::

     T = TypeVar('T')  # Can be anything
     S = TypeVar('S', bound=str)  # Can be any subtype of str
     A = TypeVar('A', str, bytes)  # Must be exactly str or bytes

  Type variables exist primarily for the benefit of static type
  checkers.  They serve as the parameters for generic types as well
  as for generic function and type alias definitions.

  The variance of type variables is inferred by type checkers when they
  are created through the type parameter syntax and when
  ``infer_variance=True`` is passed. Manually created type variables may
  be explicitly marked covariant or contravariant by passing
  ``covariant=True`` or ``contravariant=True``. By default, manually
  created type variables are invariant. See PEP 484 and PEP 695 for more
  details.



  FSMTransitionEvent(old_state, new_state, event, when, extra)

  *`FSMTransitionEvent.event`*:
  Alias for field number 2

  *`FSMTransitionEvent.extra`*:
  Alias for field number 4

  *`FSMTransitionEvent.new_state`*:
  Alias for field number 1

  *`FSMTransitionEvent.old_state`*:
  Alias for field number 0

  *`FSMTransitionEvent.when`*:
  Alias for field number 3





  *Release 20240721.1*:
  FSM.fsm_print_state_diagram: bugfix: plumb fmt through to gvprint.

  *Release 20240721*:
  FSM: new fsm_print_state_diagram method to write the state diagram out in various formats.

  *Release 20240712*:
  FSM: make fsm_transitions_as_dot a shim for new fsm_state_diagram_as_dot class method.

  *Release 20240630*:
  * CancellationError from cs.result to cs.fsm, and use it to catch silently FSM event callbacks which raise it.
  * Some other minor churn.

  *Release 20240519*:
  FSM: default for FSM_DEFAULT_STATE is the first key from FSM_TRANSITIONS (relies on ordered dicts, so Python 3.6 onward).

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240305*:
  FSM.__getattr__: return None for missing self.fsm_state, happens in too-early call to __str__.

  *Release 20231020*:
  FSM.__getattr__: known transition names no longerfall through to the superclass if not valid for the current state.

  *Release 20231018*:
  * FSM.fsm_transitions_as_dot: new optional history_style parameter to style transitioned edges.
  * FSM.fsm_as_svg: plumb optional history_style parameter.

  *Release 20230816.3*:
  Bump cs.gvutils requirement.

  *Release 20230816.2*:
  FSM.fsm_transitions_as_dot: bugfix: the style needs "style=filled" as well as the fillcolor.

  *Release 20230816.1*:
  FSM.fsm_transitions_as_dot: now an instance method so that we can colour the current state.

  *Release 20230816*:
  FSM: new fsm_as_svg method and fsm_svg property.

  *Release 20221118*:
  * FSM.__init__: make state optional, default from self.FSM_DEFAULT_STATE - now all args are optional.
  * FSM.__init__: if the state is None or not supplied, do not set .fsm_state at all; add explaination for this weird design choice.
  * FSM.__getattr__: only generate event methods for events with public names (no leading underscore).
  * FSM: new .fsm_history property, aiding subclassing elsewhere.
  * FSM: drop dot_node_fillcolor, now provided by DOTNodeMixin.__getattr__, provide dot_node_palette_key using self.fsm_state.
  * FSM.dot_node_attrs: color from self.dot_node_color.

  *Release 20220918*:
  Replace callback exception warning() with exception() for the traceback.

  *Release 20220805.1*:
  * FSM: subclass DOTNodeMixin and provide a hook for a colour palette for node fillcolors.
  * Other minor changes.

  *Release 20220805*:
  Initial PyPI release.


pkgId: f487d7effb2e3bb1a41a85e6536d580b092cfb2d9c34b55958ce30915c6841b6
name: python-cs.fstags
arch: src
version: 20241005
epoch: 0
release: 1
summary: Simple filesystem based file tagging and the associated `fstags` command line script.
url: https://pypi.org/project/cs.fstags/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.fstags-20241005-1.src.rpm
checksum_type: sha256
description:
  Simple filesystem based file tagging
  and the associated `fstags` command line script.

  *Latest release 20241005*:
  Assorted minor changes.

  Many basic tasks can be performed with the `fstags` command line utility,
  documented under the `FSTagsCommand` class below.

  Why `fstags`?
  By storing the tags in a separate file we:
  * can store tags without modifying a file
  * do not need to know the file's format,
    or even whether that format supports metadata
  * can process tags on any kind of file
  * because tags are inherited from parent directories,
    tags can be automatically acquired merely by arranging your file tree

  Tags are stored in the file `.fstags` in each directory;
  there is a line for each entry in the directory
  consisting of the directory entry name and the associated tags.

  Tags may be "bare", or have a value.
  If there is a value it is expressed with an equals (`'='`)
  followed by the JSON encoding of the value.

  The tags for a file are the union of its direct tags
  and all relevant ancestor tags,
  with priority given to tags closer to the file.

  For example, a media file for a television episode with the pathname
  `/path/to/series-name/season-02/episode-name--s02e03--something.mp4`
  might have the tags:

      series_title="Series Full Name"
      season=2
      sf
      episode=3
      episode_title="Full Episode Title"

  obtained from the following `.fstags` entries:
  * tag file `/path/to/.fstags`:

      series-name sf series_title="Series Full Name"

  * tag file `/path/to/series-name/.fstags`:

    season-02 season=2

  * tag file `/path/to/series-name/season-02/.fstags`:

    episode-name--s02e03--something.mp4 episode=3 episode_title="Full Episode Title"





  Walk the media tree for files tagged for backup to `archive2`:

      fstags find /path/to/media backup=archive2

  Walk the media tree for files not assigned to a backup archive:

      fstags find /path/to/media -backup

  Backup the `archive2` files using `rsync`:

      fstags find --for-rsync /path/to/media backup=archive2 \
      | rsync -ia --include-from=- /path/to/media /path/to/backup_archive2



  Manage a cached value stored in a `TaggedPath`.
  The value and the validity state are stored in a prefixed
  subsection of the tags.

  This is how modules like `cs.hashindex` cache file content hashcodes.

  The default state function is `TaggedPath.stat_size_mtime`,
  which returns `{'st_size':st_size,'st_mtime':int(st_mtime)}` by default,
  essentially the same criteria used by `rsync(1)` to skip
  comparing file contents.

  Example:

        tags = fstags[fspath]
        hash

  *`CachedValue.__init__(self, taggedpath: cs.fstags.TaggedPath, prefix: str, name: str, *, state_func: Optional[Callable[[str], Mapping[str, Any]]] = None)`*:
  Initialise a cached value reference.

  Parameters:
  * `taggedpath`: the `TaggedPath` tagged file system path
  * `prefix`: the tags prefix
  * `name`: the tag name for the value
  * `state_func`; an optional function to compute the current
    state if `taggedpath`; the default is
    `CachedValue.stat_size_mtime` which returns the current
    file `st_size` and `st_mtime`

  *`CachedValue.get(self) -> Tuple[Optional[Any], Mapping[str, Any]]`*:
  Get the cached value if the current state matches the cache
  state, otherwise `None`.
  Return the valu or `None` and the current state.

  *`CachedValue.set(self, value: Any, *, state: Mapping = None)`*:
  Update the cached value and associated state.
  If the state is omitted, the current state is used.

  *`CachedValue.stat_size_mtime(fspath: str, round_mtime=<class 'int'>, follow_symlinks=True) -> dict`*:
  Return the default cache state mapping.
  This function `stat`s the `fspath` and returns `{'size':st_size,'mtime':int(st_mtime)}`.



  A cascade rule of possible source tag names to provide a target tag.

  *`CascadeRule.infer_tag(self, tagset)`*:
  Apply the rule to the `TagSet` `tagset`.
  Return a new `Tag(self.target,value)`
  for the first cascade `value` found in `tagset`,
  or `None` if there is no match.



  A class to examine filesystem tags.



  A class to examine filesystem tags.

  *`FSTags.__init__(self, tagsfile_basename=None, ontology_filepath=None, physical=None, update_mapping: Optional[Mapping] = None, update_prefix: Optional[str] = 'cs.fstags', update_uuid_tag_name: Optional[str] = 'uuid')`*:
  Initialise the `FSTags` instance.

  Parameters:
  * `tagsfile_basename`: optional basename forthe backing tags files,
    default from `TAGSFILE_BASENAME`: `'.fstags'`
  * `ontology_filepath`: optional filesystem path for an associated ontology
  * `physical`: optional flag for the associated `FSTagsConfig`
    specifying whether `TagFile`s are indexed by their physical or logical
    filesystem paths
  * `update_mapping`: optional secondary mapping to which to mirror
    tags, such as an `SQLTags`;
    the default comes from an `SQLTags` specified by the
    environment variable `$FSTAGS_UPDATE_MAPPING`
    if present
  * `update_prefix`: optional key prefix for use in the secondary mapping;
    the default comes from the environment variable
    `$FSTAGS_UPDATE_MAPPING_PREFIX` if present,
    otherwise `'cs.fstags'`
  * `update_uuid_tag_name`: optional name for the per file UUID tag name;
    default `'uuid'`

  *`FSTags.__getitem__(self, path) -> 'TaggedPath'`*:
  Return the `TaggedPath` for `abspath(path)`.

  *`FSTags.apply_tag_choices(self, tag_choices, paths)`*:
  Apply the `tag_choices` to `paths`.

  Parameters:
  * `tag_choices`:
    an iterable of `Tag` or an equality `TagBasedTest`.
    Each item applies or removes a `Tag`
    from each path's direct tags.
  * `paths`:
    an iterable of filesystem paths.

  *`FSTags.attach_path(self, attach, srcpath, dstpath, *, force=False, crop_ok=False)`*:
  Attach `srcpath` to `dstpath` using the `attach` callable.

  Parameters:
  * `attach`: callable accepting `attach(srcpath,dstpath)`
    to do the desired attachment,
    such as a copy, link or move
  * `srcpath`: the source filesystem object
  * `dstpath`: the destination filesystem object
  * `crop_ok`: if true and the OS raises `OSError(ENAMETOOLONG)`
    attempt to crop the name before the file extension and retry
  * `force`: default `False`.
    If true and the destination exists
    try to remove it before calling `attach`.
    Otherwise if the destination exists
    raise a `ValueError`.

  *`FSTags.cascade_tags(self, tags, cascade_rules=None)`*:
  Yield `Tag`s
  which cascade from the `TagSet` `tags`
  via `cascade_rules` (an iterable of `CascadeRules`).

  *`FSTags.copy(self, srcpath, dstpath, **kw)`*:
  Copy `srcpath` to `dstpath`.

  *`FSTags.dir_tagfile(self, dirpath: str) -> 'FSTagsTagFile'`*:
  Return the `FSTagsTagFile` associated with `dirpath`.

  *`FSTags.edit_dirpath(self, dirpath, all_names=False)`*:
  Edit the filenames and tags in a directory.

  If `all_names` is true, include names commencing with a dot,
  otherwise exclude them.

  *`FSTags.export_xattrs(self, paths)`*:
  Import the extended attributes of `paths`
  and use them to update the fstags.

  *`FSTags.find(self, path, tag_tests, use_direct_tags=False)`*:
  Walk the file tree from `path`
  searching for files matching the supplied `tag_tests`.
  Yield the matching file paths.

  Parameters:
  * `path`: the top of the file tree to walk
  * `tag_tests`: a sequence of `TagBasedTest`s
  * `use_direct_tags`: test the `direct_tags` if true,
    otherwise the `all_tags`.
    Default: `False`

  *`FSTags.find_ontology(self, dirpath, ontbase=None)`*:
  Locate an ontology for the directory `dirpath`.
  The optional `ontbase` may override the relative path to the file,
  default is `self.ontology_filepath`.
  Return a `TagOntology` or `None` if not found.

  *`FSTags.find_ontpath(self, dirpath, ontbase=None)`*:
  Locate an ontology file for the directory `dirpath`.
  The optional `ontbase` may override the relative path to the file,
  default is `self.ontology_filepath`.
  Return the found ontology file or `None` if not found.

  *`FSTags.import_xattrs(self, paths)`*:
  Update the extended attributes of `paths`
  from their fstags.

  *`FSTags.keypath(self, fspath)`*:
  Compute the absolute path used to index a `TaggedPath` instance.

  This returns `realpath(fspath)` if `self.config.physical`,
  otherwise `abspath(fspath)`.

  *`FSTags.link(self, srcpath, dstpath, **kw)`*:
  Link `srcpath` to `dstpath`.

  *`FSTags.move(self, srcpath, dstpath, **kw)`*:
  Move `srcpath` to `dstpath`.

  *`FSTags.mv(self, srcpath: str, dstpath: str, *, symlink=False, remove=True)`*:
  Move (or link or symlink) `srcpath` to `dstpath`.

  Parameters:
  * `srcpath`: the source filesystem path
  * `dstpath`: the destination filesystem path
  * `symlink`: default `False`: if true, make a symbolic link
  * `remove`: default `True`: if true, remove `srcpath` after
    hard linking to `dstpath`

  *`FSTags.ontology`*:
  The primary `TagsOntology`, or `None` if `self.ontology_filepath` was `None`.

  *`FSTags.ontology_filepath`*:
  The ontology file basename.

  *`FSTags.ontology_for(self, path, ontbase=None)`*:
  Return the `TagsOntology` associated with `path`.
  Returns `None` if an ontology cannot be found.

  *`FSTags.open_ontology(self, ontpath)`*:
  Open the contology file at `ontpath`.

  *`FSTags.path_tagfiles(self, fspath)`*:
  Generator yielding a sequence of `(FSTagsTagFile,name)` pairs
  where `name` is the key within the `FSTagsTagFile`
  for the `FSTagsTagFile`s affecting `fspath`
  in order from the root to `dirname(fspath)`.

  *`FSTags.resolve_format_string(self, format_string)`*:
  See if `format_string` looks like `[`*clausename*`]`*entryname*.
  if so, return the corresponding config entry string,
  otherwise return `format_string` unchanged.

  *`FSTags.scrub(self, path)`*:
  Scrub tags for names which do not exist in the filesystem.

  *`FSTags.startup_shutdown(self)`*:
  Sync tag files and db mapping on final close.

  *`FSTags.sync(self)`*:
  Flush modified tag files.

  *`FSTags.tagfile_for(self, fspath)`*:
  Return the `FSTagsTagFile` storing the `Tag`s for `fspath`.

  *`FSTags.tagsfile_basename`*:
  The tag file basename.

  *`FSTags.test(self, path, tag_tests, use_direct_tags=False)`*:
  Test a path against `tag_tests`.

  Parameters:
  * `path`: path to test
  * `tag_tests`: an iterable of `TagBasedTest`s
  * `use_direct_tags`: test the `direct_tags` if true,
    otherwise the `all_tags`.
    Default: `False`



  `fstags` main command line utility.



  Usage summary:

      Usage: fstags [-o ontology] [-P] subcommand [...]
        -o ontology   Specify the path to an ontology file.
        -P            Physical. Resolve pathnames through symlinks.
                      Default ~/.fstagsrc[general]physical or False.
        Subcommands:
          autotag paths...
            Tag paths based on rules from the rc file.
          cp [-finv] srcpath dstpath, cp [-finv] srcpaths... dstdirpath
            POSIX cp(1) equivalent, but also copying tags: copy files and their tags into targetdir.
            -f  Force: remove destination if it exists.
            -i  Interactive: fail if the destination exists.
            -n  No remove: fail if the destination exists.
            -v  Verbose: show copied files.
          cptags srcpath dstpath
            Copy the direct tags from srcpath to dstpath.
          edit [-ad] [path]
            Edit the direct tagsets of path, default: '.'.
            If path is a directory, provide the tags of its entries.
            Otherwise edit just the tags for path.
            -a    List all names in directory edit mode; normally
                  names commencing with a dot are omitted.
            -d    Treat directories like files: edit just its tags.
          export [-a] [--direct] path {tag[=value]|-tag}...
            Export tags for files from paths matching all the constraints.
            -a        Export all paths, not just those with tags.
            --direct  Export the direct tags instead of the computed tags.
            The output is in the same CSV format as that from "sqltags export",
            with the following columns:
            * unixtime: the file's st_mtime from os.stat.
            * id: empty
            * name: the file path
            * tags: the file's direct or indirect tags
          find [--direct] [--for-rsync] [-o output_format] path {tag[=value]|-tag}...
            List files from path matching all the constraints.
            --direct    Use direct tags instead of all tags.
            --for-rsync Instead of listing matching paths, emit a
                        sequence of rsync(1) patterns suitable for use with
                        --include-from in order to do a selective rsync of the
                        matched paths.
            -o output_format
                        Use output_format as a Python format string to lay out
                        the listing.
                        Default: {fspath}
          help [-l] [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 even if no subcommand-names provided.
          import {-|srcpath}...
            Import CSV data in the format emitted by "export".
            Each argument is a file path or "-", indicating standard input.
          infer pathname
            Print the base and inferred tags for pathname.
          json_import --prefix=tag_prefix {-|path} {-|tags.json}
            Apply JSON data to path.
            A path named "-" indicates that paths should be read from
            the standard input.
            The JSON tag data come from the file "tags.json"; the name
            "-" indicates that the JSON data should be read from the
            standard input.
          ln [-finv] srcpath dstpath, ln [-finv] srcpaths... dstdirpath
            POSIX ln(1) equivalent, but also copying the tags: link files and their tags into targetdir.
            -f  Force: remove destination if it exists.
            -i  Interactive: fail if the destination exists.
            -n  No remove: fail if the destination exists.
            -v  Verbose: show linked files.
          ls [-dlr] [--direct] [-o output_format] [paths...]
            List files from paths and their tags.
            -d          Treat directories like files, do not recurse.
            --direct    List direct tags instead of all tags.
            -l          Long format.
            -o output_format
                        Use output_format as a Python format string to lay out
                        the listing.
                        Default: {fspath:json} {tags}
            -r          Recurse into subdirectories.
          mv [-finv] srcpath dstpath, mv [-finv] srcpaths... dstdirpath
            POSIX mv(1) equivalent, but also copying the tags: move files and their tags into targetdir.
            -f  Force: remove destination if it exists.
            -i  Interactive: fail if the destination exists.
            -n  No remove: fail if the destination exists.
            -v  Verbose: show moved files.
          ns [-d] [--direct] [paths...]
            Report on the available primary namespace fields for formatting.
            Note that because the namespace used for formatting has
            inferred field names there are also unshown secondary field
            names available in format strings.
            -d          Treat directories like files, do not recurse.
            --direct    List direct tags instead of all tags.
          ont [subcommand [args...]]
            With no arguments, print the ontology.
          rename -o basename_format paths...
            Rename paths according to a format string.
            -o basename_format
                Use basename_format as a Python format string to
                compute the new basename for each path.
          scrub paths...
            Remove all tags for missing paths.
            If a path is a directory, scrub the immediate paths in the directory.
          shell
            Run a command prompt via cmd.Cmd using this command's subcommands.
          tag {-|path} {tag[=value]|-tag}...
            Tag a path with multiple tags.
            With the form "-tag", remove that tag from the direct tags.
            A path named "-" indicates that paths should be read from the
            standard input.
          tagfile tagfile_path [subcommand ...]
            Perform operations on a tag file.
            Subcommands:
              tag tagset_name {tag[=value]|-tag}...
                Directly modify tag_name within the tag file tagfile_path.
          tagpaths {tag[=value]|-tag} {-|paths...}
            Tag multiple paths.
            With the form "-tag", remove the tag from the immediate tags.
            A single path named "-" indicates that paths should be read
            from the standard input.
          test [--direct] path {tag[=value]|-tag}...
            Test whether the path matches all the constraints.
            --direct    Use direct tags instead of all tags.
          xattr_export {-|paths...}
            Import tag information from extended attributes.
          xattr_import {-|paths...}
            Update extended attributes from tags.

  *`FSTagsCommand.Options`*

  *`FSTagsCommand.apply_opt(self, opt, val)`*:
  Apply command line option.

  *`FSTagsCommand.cmd_autotag(self, argv, *, upd: cs.upd.Upd, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x108d0e700>)`*:
  Usage: {cmd} paths...
  Tag paths based on rules from the rc file.

  *`FSTagsCommand.cmd_cp(self, argv)`*:
  Usage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath
  POSIX cp(1) equivalent, but also copying tags: copy files and their tags into targetdir.
  -f  Force: remove destination if it exists.
  -i  Interactive: fail if the destination exists.
  -n  No remove: fail if the destination exists.
  -v  Verbose: show copied files.

  *`FSTagsCommand.cmd_cptags(self, argv)`*:
  Usage: {cmd} srcpath dstpath
  Copy the direct tags from srcpath to dstpath.

  *`FSTagsCommand.cmd_edit(self, argv)`*:
  Usage: {cmd} [-ad] [path]
  Edit the direct tagsets of path, default: '.'.
  If path is a directory, provide the tags of its entries.
  Otherwise edit just the tags for path.
  -a    List all names in directory edit mode; normally
        names commencing with a dot are omitted.
  -d    Treat directories like files: edit just its tags.

  *`FSTagsCommand.cmd_export(self, argv, *, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x108d0ec00>)`*:
  Usage: {cmd} [-a] [--direct] path {{tag[=value]|-tag}}...
  Export tags for files from paths matching all the constraints.
  -a        Export all paths, not just those with tags.
  --direct  Export the direct tags instead of the computed tags.
  The output is in the same CSV format as that from "sqltags export",
  with the following columns:
  * unixtime: the file's st_mtime from os.stat.
  * id: empty
  * name: the file path
  * tags: the file's direct or indirect tags

  *`FSTagsCommand.cmd_find(self, argv, *, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x108d0ede0>)`*:
  Usage: {cmd} [--direct] [--for-rsync] [-o output_format] path {{tag[=value]|-tag}}...
  List files from path matching all the constraints.
  --direct    Use direct tags instead of all tags.
  --for-rsync Instead of listing matching paths, emit a
              sequence of rsync(1) patterns suitable for use with
              --include-from in order to do a selective rsync of the
              matched paths.
  -o output_format
              Use output_format as a Python format string to lay out
              the listing.
              Default: {FIND_OUTPUT_FORMAT_DEFAULT}

  *`FSTagsCommand.cmd_import(self, argv)`*:
  Usage: {cmd} {{-|srcpath}}...
  Import CSV data in the format emitted by "export".
  Each argument is a file path or "-", indicating standard input.

  *`FSTagsCommand.cmd_infer(self, argv)`*:
  Usage: {cmd} pathname
  Print the base and inferred tags for pathname.

  *`FSTagsCommand.cmd_json_import(self, argv)`*:
  Usage: json_import --prefix=tag_prefix {{-|path}} {{-|tags.json}}
  Apply JSON data to path.
  A path named "-" indicates that paths should be read from
  the standard input.
  The JSON tag data come from the file "tags.json"; the name
  "-" indicates that the JSON data should be read from the
  standard input.

  *`FSTagsCommand.cmd_ln(self, argv)`*:
  Usage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath
  POSIX ln(1) equivalent, but also copying the tags: link files and their tags into targetdir.
  -f  Force: remove destination if it exists.
  -i  Interactive: fail if the destination exists.
  -n  No remove: fail if the destination exists.
  -v  Verbose: show linked files.

  *`FSTagsCommand.cmd_ls(self, argv, *, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x108d0f240>)`*:
  Usage: {cmd} [-dlr] [--direct] [-o output_format] [paths...]
  List files from paths and their tags.
  -d          Treat directories like files, do not recurse.
  --direct    List direct tags instead of all tags.
  -l          Long format.
  -o output_format
              Use output_format as a Python format string to lay out
              the listing.
              Default: {LS_OUTPUT_FORMAT_DEFAULT}
  -r          Recurse into subdirectories.

  *`FSTagsCommand.cmd_mv(self, argv)`*:
  Usage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath
  POSIX mv(1) equivalent, but also copying the tags: move files and their tags into targetdir.
  -f  Force: remove destination if it exists.
  -i  Interactive: fail if the destination exists.
  -n  No remove: fail if the destination exists.
  -v  Verbose: show moved files.

  *`FSTagsCommand.cmd_ns(self, argv, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x108d0f6a0>)`*:
  Usage: {cmd} [-d] [--direct] [paths...]
  Report on the available primary namespace fields for formatting.
  Note that because the namespace used for formatting has
  inferred field names there are also unshown secondary field
  names available in format strings.
  -d          Treat directories like files, do not recurse.
  --direct    List direct tags instead of all tags.

  *`FSTagsCommand.cmd_ont(self, argv)`*:
  Ontology operations.

  Usage: {cmd} [subcommand [args...]]
    With no arguments, print the ontology.

  *`FSTagsCommand.cmd_rename(self, argv)`*:
  Usage: {cmd} -o basename_format paths...
  Rename paths according to a format string.
  -o basename_format
      Use basename_format as a Python format string to
      compute the new basename for each path.

  *`FSTagsCommand.cmd_scrub(self, argv)`*:
  Usage: {cmd} paths...
  Remove all tags for missing paths.
  If a path is a directory, scrub the immediate paths in the directory.

  *`FSTagsCommand.cmd_tag(self, argv)`*:
  Usage: {cmd} {{-|path}} {{tag[=value]|-tag}}...
  Tag a path with multiple tags.
  With the form "-tag", remove that tag from the direct tags.
  A path named "-" indicates that paths should be read from the
  standard input.

  *`FSTagsCommand.cmd_tagfile(self, argv)`*:
  Usage: {cmd} tagfile_path [subcommand ...]
  Perform operations on a tag file.
  Subcommands:
    tag tagset_name {{tag[=value]|-tag}}...
      Directly modify tag_name within the tag file tagfile_path.

  *`FSTagsCommand.cmd_tagpaths(self, argv)`*:
  Usage: {cmd} {{tag[=value]|-tag}} {{-|paths...}}
  Tag multiple paths.
  With the form "-tag", remove the tag from the immediate tags.
  A single path named "-" indicates that paths should be read
  from the standard input.

  *`FSTagsCommand.cmd_test(self, argv)`*:
  Usage: {cmd} [--direct] path {{tag[=value]|-tag}}...
  Test whether the path matches all the constraints.
  --direct    Use direct tags instead of all tags.

  *`FSTagsCommand.cmd_xattr_export(self, argv)`*:
  Usage: {cmd} {{-|paths...}}
  Import tag information from extended attributes.

  *`FSTagsCommand.cmd_xattr_import(self, argv)`*:
  Usage: {cmd} {{-|paths...}}
  Update extended attributes from tags.

  *`FSTagsCommand.import_csv_file(self, f, *, convert_name=None)`*:
  Import CSV data from the file `f`.

  Parameters:
  * `f`: the source CSV file
  * `convert_name`: a callable to convert each input name
    into a file path; the default is to use the input name directly

  *`FSTagsCommand.run_context(self)`*:
  Push the `FSTags`.



  A configuration for fstags.

  *`FSTagsConfig.__init__(self, rcfilepath=None, physical=None)`*:
  Initialise the config.

  Parameters:
  * `rcfilepath`: the path to the confguration file
    If `None`, default to `'~/.fstagsrc'` (from `RCFILE`).

  *`FSTagsConfig.cascade_rules_from_config(config)`*:
  Return a list of the `[cascade]` tag rules from the config.

  *`FSTagsConfig.filename_rules_from_config(config)`*:
  Return a list of the `[filename_autotag]` tag rules from the config.

  *`FSTagsConfig.load_config(rcfilepath)`*:
  Read an rc file, return a `ConfigParser` instance.

  *`FSTagsConfig.tagsfile_basename`*:
  The tags filename, default `'.fstags'`.



  A `FSTagsTagFile` indexing `TagSet`s for file paths
  which lives in the file path's directory.

  *`FSTagsTagFile.TagSetClass(self, name: str) -> cs.fstags.TaggedPath`*:
  factory to create a `TaggedPath` from a `name`.

  *`FSTagsTagFile.dirpath`*:
  Return the path of the directory associated with this `FSTagsTagFile`.



  Read the extended attribute `xattr_name` of `fspath`.
  Return the extended attribute value as a string,
  or `None` if the attribute does not exist.

  Parameters:
  * `fspath`: the filesystem path to update
  * `xattr_name`: the extended attribute to obtain
    if this is a `str`, the attribute is the UTF-8 encoding of that name.



  Mixin providing an automatic `.fstags` property.

  *`HasFSTagsMixin.fstags`*:
  Return the `.fstags` property,
  default a shared default `FSTags` instance.



  Test whether `name` is a valid basefile for something in a directory.



  Command line mode.



  Return a list of rsync include lines
  suitable for use with the `--include-from` option.



  Class to manipulate the tags for a specific filesystem path.

  *`TaggedPath.all_tags`*:
  Cached cumulative tags for this path as a `TagSet`
  by merging the tags from the root to the path.

  Note that subsequent changes to some path component's `direct_tags`
  will not affect this `TagSet`.

  *`TaggedPath.as_tags(self, prefix=None, all_tags=False)`*:
  Yield the tag data as `Tag`s.

  This overrides `TagSet.as_tags`,
  honouring an `optional `all_tags` parameter.

  *`TaggedPath.auto_infer(self, attr)`*:
  Infer a value from `attr` via the associated `FSTags.cascade_rules`.

  This implementation tries the cascade rules from the configuration
  and falls back to the superclass inference (the direct tag, if present).
  Therefore a tagset with a rule for `.title` and also a
  direct `.title` Tag would return the direct tag value for
  `.title` and the rule value for `.auto.title`.

  *`TaggedPath.basename`*:
  The name of the final path component.

  *`TaggedPath.cached_value(self, prefix: str, name: str, *, state_func: Optional[Callable[[str], Mapping[str, Any]]] = None) -> 'CachedValue'`*:
  Return `CachedValue` managing the  `prefix.name` tag.

  *`TaggedPath.export_xattrs(self)`*:
  Update the extended attributes of the file.

  *`TaggedPath.findup(self, check)`*:
  Locate the first `TaggedPath` from `self` upwards (via `.parent`)
  for which `check(TaggedPath)` is true.

  *`TaggedPath.format_kwargs(self, *, direct=False)`*:
  Format arguments suitable for `str.format_map`.

  This returns an `ExtendedNamespace` from `TagSet.ns()`
  for a computed `TagSet`.

  In addition to the normal `TagSet.ns()` names
  the following additional names are available:
  * `fspath.basename`: basename of the `TaggedPath.fspath`
  * `fspath.pathname`: the `TaggedPath.fspath`
  * `fspath.encoded`: the JSON encoded fspath
  * `tags`: the `TagSet` as a string

  *`TaggedPath.format_tagset(self, *, direct=False)`*:
  Compute a `TagSet` from this file's tags
  with additional derived tags.

  This can be converted into an `ExtendedNamespace`
  suitable for use with `str.format_map`
  via the `TagSet`'s `.format_kwargs()` method.

  In addition to the normal `TagSet.ns()` names
  the following additional names are available:
  * `fspath.basename`: basename of the `TaggedPath.fspath`
  * `fspath.ext`: the file extension of the basename
    of the `TaggedPath.fspath`
  * `fspath.pathname`: the `TaggedPath.fspath`
  * `fspath.encoded`: the JSON encoded fspath

  *`TaggedPath.from_str(fspath, *, fstags: Optional[cs.fstags.FSTags] = <function <lambda> at 0x108d0e200>)`*:
  Supports the `@promote` decorator.

  *`TaggedPath.get_xattr_tagset(self, xattr_name=None)`*:
  Return a new `TagSet`
  from the extended attribute `xattr_name` of `self.fspath`.
  The default `xattr_name` is `XATTR_B` (`None`).

  *`TaggedPath.import_xattrs(self)`*:
  Update the direct tags from the file's extended attributes.

  *`TaggedPath.infer_from_basename(self, rules=None)`*:
  Apply `rules` to the basename of this `TaggedPath`,
  return a `TagSet` of inferred `Tag`s.

  Tag values from earlier rules override values from later rules.

  The default rules come from `self.fstags.config.filename_rules`,
  which is sourced from `~/.fstagsrc`.

  *`TaggedPath.infer_tags(self)`*:
  Infer tags for this path.

  In order of preference:
  * from filesystem fstags
  * from file basename matching
  * from various `_type` suffixes
  * from the cascade rules (see `FSTags.cascade_rules`)

  *`TaggedPath.keypath`*:
  The key path used to index this `TaggedPath` within its `FSTags`
  from `FSTags.keypath(path)`.

  *`TaggedPath.merged_tags(self)`*:
  Compute the cumulative tags for this path as a new `TagSet`
  by merging the tags from the root to the path.

  *`TaggedPath.name`*:
  The `.name` is `basename(self.fspath)`.

  *`TaggedPath.parent`*:
  A reference to the parent of this `TaggedPath`, or `None`.

  *`TaggedPath.prune_inherited(self)`*:
  Examine the tags of this path's parent.
  Remove any tag on this file if they are present on the parent.
  Return a `TagSet` containing the pruned `Tag`s.

  *`TaggedPath.save(self, prune=True)`*:
  Update the associated `FSTagsTagFile`.

  *`TaggedPath.save_if_closed(self, **save_kw)`*:
  Save the tag file is `self._fstags` is closed (no autosave).

  *`TaggedPath.set(self, tag_name, value, **kw)`*:
  Forbid the special tag name `'name'`, reserved for the filename.

  *`TaggedPath.tagfile`*:
  Return the `FSTagsTagFile` storing the state for this `TaggedPath`.

  *`TaggedPath.update(self, other=None, **update_kw)`*:
  Call `TagSet.update` with the `FSTags` open.



  Update the extended attributes of `fspath`
  with `new_xattr_value` for `xattr_name`.
  Return the previous value, or `None` if the attribute was missing.

  We avoid calling `os.setxattr` if the value will not change.

  Parameters:
  * `fspath`: the filesystem path to update
  * `xattr_name`: the extended attribute to update;
    if this is a `str`, the attribute is the UTF-8 encoding of that name.
  * `new_xattr_value`: the new extended attribute value, a `str`
    which should be the transcription of `TagSet`
    i.e. `str(tagset)`



  Emit message if in verbose mode.





  *Release 20241005*:
  Assorted minor changes.

  *Release 20240709*:
  * New CachedValue class to manage a cached TaggedPath tag value.
  * New TaggedPath.cached_value() method to return a CachedValue.

  *Release 20240422*:
  * Bugfix: .fstags files should no longer get Python repr() output for weirdly typed tag values.
  * Some other minor internal updates.

  *Release 20240412*:
  Changes to accomodate dropping BaseCommandOptions.runstate.

  *Release 20240316*:
  * TaggedPath.infer_tags: add _dt, _date, _f conversions, skip conversions which raise TypeError or ValueError.
  * rpaths: fix to yield relative paths.

  *Release 20240211*:
  * TaggedPath: subclass Promotable, add from_str(fspath) class method.
  * FSTags: new mv(srcpath,dstpath) method to move/link/symlink a file bringing its tags.

  *Release 20240201.1*:
  Release with "fstags" script.

  *Release 20240201*:
  FSTags.attach_path: do not save the tagfile if the FSTags is open - the final close will do it.

  *Release 20231129*:
  FSTags.startup_shutdown: run self.sync() in a finally clause.

  *Release 20230407*:
  * FSTags: support open/close of self.update_mapping, update startup/shutdown to startup_shutdown.
  * Move the (optional) ORM open/close from FSTags.startup_shutdown to TagFile.save, greatly shortens the ORM lock.

  *Release 20230217*:
  FSTagsCommand.cmd_rename: use -o for the format string as for other commands, -n is for "no action".

  *Release 20230212*:
  * FSTags.keypath(fspath) and TaggedPath.keypath with the singleton filesystem path for a TaggedPath.
  * TaggedPath.parent property.
  * TaggedPath.findup(check) method.
  * FSTagsCommand: cmd_find,cmd_ls: abort if runstate.cancelled thus honouring SIGINT/^C.

  *Release 20230211*:
  FSTags.__init__: treat empty $FSTAGS_UPDATE_MAPPING as missing i.e. None.

  *Release 20230210*:
  New optional update_mapping for mirroring tags eg to an SQLTags; activated automatically by an $FSTAGS_UPDATE_MAPPING environment variable.

  *Release 20221228*:
  TaggedPath.save: new options prune=True parameter to drop empty top level dict/lists.

  *Release 20220918*:
  * FSTagsConfig: add empty .provided dict for config overrides.
  * FSTagsConfig: set FSPATH_DEFAULT=RCFILE for use by FSPathBasedSingleton._singleton_key.
  * FSTagsConfig: accept optional `physical` parameter to choose `realpath` over `abspath` and new `.physical` property.
  * FSTags.__getitem__: use `abspath` or `realpath` depending on `self.config.physical`.
  * FSTagsCommand: new -P option for "physical" mode, plumb though to the config and self.options.
  * Provide a DEFAULT_FSTAGS instance and an @uses_fstags decorator.

  *Release 20220606*:
  FSTagsCommand.edit: mention the filename in the header comments in tagset edit mode.

  *Release 20220430*:
  Minor bugfixes.

  *Release 20220311*:
  * New TaggedPath.infer_tags() method to compute and return the inferred TagSet.
  * New "fstags infer pathname" command.

  *Release 20211212*:
  * Rename edit_many to edit_tagsets for clarity.
  * FSTags.edit_dirpath: include realpath(dirpath) at the top of the edit list.

  *Release 20210906*:
  * TaggedPath: new auto_infer method overriding the inherited TagSet.auto_infer which consults the cascade_rules from the .fstagsrc.
  * fstags ls: new -l option to print a multiline tag listing.
  * fstags cptags: copy the tags from one path to another.
  * Assorted other changes.

  *Release 20210404*:
  * FSTags.edit_dirpath: new all_names parameter to include dot-names.
  * FSTagsCommand.cmd_edit: new -a option to turn on the all_names parameter.

  *Release 20210306*:
  * FSTags: new tagfile_for(filepath) to obtain the TagFile for filepath.
  * FSTagsCommand: new -o ontology option to supply an ontology file for the FSTags.
  * FSTagsCommand.cmd_ont: drop -o/--ontology, superceded by global -o option.
  * Move BaseTagFile from cs.fstags to TagFile in cs.tagset.
  * FSTags.edit_dirpath: we now get (old_name,new_name,TaggedPath) back from edit_many, obviating the te_id_map.
  * TaggedPath: .name property returning basename(self.filepath), .set and .discard methods rejecting use of the 'name' tag.
  * FSTagsCommand: port to new cs.cmdutils API.
  * Many small refactors and bugfixes.

  *Release 20200717.1*:
  Add the manual page MarkDown source files. Note: well out of date, need updating.

  *Release 20200717*:
  DISTINFO: require cs.obj>=20200716 for SingletonMixin API change.

  *Release 20200716*:
  * Update for changed cs.obj.SingletonMixin API.
  * fstags: new "export" and "import" subcommands roughly paralleling those from "sqltags", providing CSV formatted export/import.
  * fstags export: ew -a (all paths) option - the default is not to not export paths with no tags.

  *Release 20200521.1*:
  fix DISTINFO.install_requires

  *Release 20200521*:
  * Add -i option to cp,ln,mv for command line compatibility, just disables -f.
  * New "rename" subcommand to rename files according to a format string.
  * Ontology support (optional).
  * Various classes are now singletons to avoid dissonance.
  * Fold "edittags" subcommand into "edit" via the "-d" (directories like files) option.
  * New "ns" subcommand reporting on the primary names available for formatting.
  * Accept [clausename]entryname as a format string to obtain the string from that entry of the config file.
  * Many bugfixes and improvements.

  *Release 20200229*:
  * New TaggedPath.modified property aliasing the TagSet.modified attribute.
  * ls: new -d option to treat directories like files (do not recurse), aiding reporting of tags for a directory.
  * find,ls subcommands: work off the realpath of the supplied top level path.
  * Tag: now subclasses namedtuple.
  * Rewrite rpaths() to use scandir and to also yield (is_dir,path) tuples.
  * TagSet, Tag, TagChoice moved into new cs.tagset module for reuse.
  * json_import: make --prefix mandatory, is "." as separator if not empty.
  * Move filename regexp rules to [filename_rules] config section.
  * New CascadeRule for representing a "target_tag_name = tag_name1 tag_name2..." config rules.
  * autotag: include the cascade rules in the autotagging after the filename rules.

  *Release 20200210*:
  * New "json_import" subcommand to import a JSON dict as tags, initial use case to load the metadata from youtube-dl.
  * New "scrub" command line operation, to purge tags of paths which do not exist.
  * New "cp", "ln" and "mv" subcommands to copy/link/move paths and take their tags with them.
  * New "test" subcommand to test paths against tag criteria, useful for find and scripts.
  * Small bugfixes.

  *Release 20200130*:
  * New FSTagsConfig class which parses the .fstagsrc as a .ini file; related adjustments.
  * New HasFSTagsMixin presenting a settable .fstags property with a shared default.
  * New xattr_import and xattr_export subcommands, remove implicit xattr access/update from other operations.
  * New TagSet.__len__ returning the number of tags.
  * Add "-" support for stdin to "tag" and "tagpaths" subcommands.

  *Release 20200113.2*:
  FSTagsCommand docstring tweak.

  *Release 20200113.1*:
  Small docstring updates.

  *Release 20200113*:
  Mirror tags to user.cs.fstags xattr to honour Linux namespace requirements. Add "filesize" to available tag string format (-o option). Small bugfixes.

  *Release 20191230*:
  * Command line: new "find" command to search a file tree based on tags.
  * Command line: new "mv" command to move a file and its tags.
  * Command line: Python string formats for "find" and "ls" output.
  * TaggedPath.autotag: new optional `no_save` parameter, default False, to suppress update of the associated .fstags file.
  * Inital and untested "mirror tags to xattrs" support.

  *Release 20191201*:
  New "edit" subcommand to rename files and edit tags.

  *Release 20191130.1*:
  Initial release: fstags, filesystem based tagging utility.


pkgId: 46b0b5f4597e3cbf970ff9689a9134339a365407ceecf793f41694649a2fa404
name: python-cs.gimmicks
arch: src
version: 20240316
epoch: 0
release: 1
summary: Gimmicks and hacks to make some of my other modules more robust and less demanding of others.
url: https://pypi.org/project/cs.gimmicks/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.gimmicks-20240316-1.src.rpm
checksum_type: sha256
description:
  Gimmicks and hacks to make some of my other modules more robust and
  less demanding of others.

  *Latest release 20240316*:
  Fixed release upload artifacts.



  Wrapper for `debug()` which does a deferred import.



  Wrapper for `error()` which does a deferred import.



  Wrapper for `exception()` which does a deferred import.



  Wrapper for `info()` which does a deferred import.



  Wrapper for `log()` which does a deferred import.



  Ghastly hack to open something for append
  entirely because some Linux systems do not let you open a
  character device for append.
  Tries sane `'a'` and falls back through 'r+' and finally to
  'w' only if `path` refers to a character device.



  Wrapper for `info()` which does a deferred import.



  Wrapper for `warning()` which does a deferred import.





  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20230331*:
  New open_append to handle idiotic Linux do-not-open-tty-for-append semantics.

  *Release 20230212*:
  Add gimmicks for cs.lex.r and cs.lex.s.

  *Release 20230210*:
  DEVNULL default open for "r+b" instead of "wb".

  *Release 20221228*:
  Generalise the logging function map, add trace function, bugfix logging call lookup.

  *Release 20220429*:
  Add DEVNULL symbol, needed before Python 3.3.

  *Release 20211208*:
  Define TimeoutError.

  *Release 20210306*:
  Add simple implementations of nullcontext and SimpleNamespace.

  *Release 20200418.1*:
  Initial release with logging call stubs.


pkgId: 55aaf035332adb33117f15350de0e7ec18c064705b5db90c081109cd5e68c469
name: python-cs.logutils
arch: src
version: 20241109
epoch: 0
release: 1
summary: Logging convenience routines.
url: https://pypi.org/project/cs.logutils/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.logutils-20241109-1.src.rpm
checksum_type: sha256
description:
  Program initialisation:
      from cs.logutils import setup_logging
      def main(argv):
          cmd = os.path.basename(argv.pop(0))
          setup_logging(cmd)
  Basic logging from anywhere:
      from cs.logutils import info, warning, error
      [...]
      def some_function(...):
          [...]
          error("nastiness found! bad value=%r", bad_value)

  Add a `FileHandler` logging to the specified `filename`;
  return the chosen logger and the new handler.
  Parameters:
  * `logger`: if supplied and not `None`, add the `FileHandler` to that
    `Logger`, otherwise to the root Logger. If `logger` is a string, call
    `logging.getLogger(logger)` to obtain the logger.
  * `mode`, `encoding` and `delay`: passed to the `FileHandler`
    initialiser.
  * `format`: used to override the handler's default format.
  * `no_prefix`: if true, do not put the `Pfx` context onto the front of the message.

  Emit a log at `logging.CRITICAL` level with the current `Pfx` prefix.

  Print formatted debug string straight to `sys.stderr` if
  `D_mode` is true, bypassing the logging modules entirely.
  A quick'n'dirty debug tool.

  Emit a log at `logging.DEBUG` level with the current `Pfx` prefix.

  Emit a log at `logging.ERROR` level with the current `Pfx` prefix.

  Emit an exception log with the current `Pfx` prefix.

  Test the `loginfo.level` against `logging.DEBUG`.

  Conditionally log a message.
  If `is_verbose` is `None`, log at `VERBOSE` level and rely on the logging setup.
  Otherwise, if `is_verbose` is true, log at `INFO` level.

  Infer a logging level from the `env_debug`, which by default
  comes from the environment variable `$DEBUG`.
  Usually default to `logging.WARNING`, but if `sys.stderr` is a terminal,
  default to `logging.INFO`.
  Parse the environment variable `$DEBUG` as a comma separated
  list of flags.
  Examine the in sequence flags to affect the logging level:
  * numeric < 1: `logging.WARNING`
  * numeric >= 1 and < 2: `logging.INFO`
  * numeric >= 2: `logging.DEBUG`
  * `"DEBUG"`: `logging.DEBUG`
  * `"STATUS"`: `STATUS`
  * `"INFO"`: `logging.INFO`
  * `"TRACK"`: `TRACK`
  * `"WARNING"`: `logging.WARNING`
  * `"ERROR"`: `logging.ERROR`
  Return an object with the following attributes:
  * `.level`: A logging level.
  * `.flags`: All the words from `$DEBUG` as separated by commas and uppercased.

  Emit a log at `logging.INFO` level with the current `Pfx` prefix.

  Emit a log at the specified level with the current `Pfx` prefix.

  Replacement for `sys.excepthook` that reports via the `cs.logutils`
  logging wrappers.

  A logging setup arranged for conventional UNIX command line use.
  *`LoggingState.__init__(self, cmd=None, main_log=None, format=None, level=None, flags=None, upd_mode=None, ansi_mode=None, trace_mode=None, verbose=None, supplant_root_logger=False)`*:
  Prepare the `LoggingState` for conventional UNIX command
  line error messaging.
  Amongst other things, the default logger now includes
  the `cs.pfx` prefix in the message.
  This function runs in two modes:
  - if logging has not been set up, it sets up a root logger
  - if the root logger already has handlers,
    monkey patch the first handler's formatter to prefix the `cs.pfx` state
  Parameters:
  * `cmd`: program name, default from `basename(sys.argv[0])`.
  * `main_log`: default logging system.
    If `None`, the main log will go to `sys.stderr`;
    if `main_log` is a string, is it used as a filename to
    open in append mode;
    otherwise main_log should be a stream suitable
    for use with `logging.StreamHandler()`.
    The resulting log handler is added to the `logging` root logger.
  * `format`: the message format for `main_log`.
    If `None`, use `DEFAULT_PFX_FORMAT_TTY`
    when `main_log` is a tty or FIFO,
    otherwise `DEFAULT_PFX_FORMAT`.
  * `level`: `main_log` logging level.
    If `None`, infer a level from the environment
    using `infer_logging_level()`.
  * `flags`: a string containing debugging flags separated by commas.
    If `None`, infer the flags from the environment using
    `infer_logging_level()`.
    The following flags have meaning:
    `D`: set cs.logutils.D_mode to True;
    `TDUMP`: attach a signal handler to SIGHUP to do a thread stack dump;
    `TRACE`: enable various noisy tracing facilities;
    `UPD`, `NOUPD`: set the default for `upd_mode` to True or False respectively.
  * `upd_mode`: a Boolean to activate cs.upd as the `main_log` method;
    if `None`, set it to `True` if `flags` contains 'UPD',
    otherwise to `False` if `flags` contains 'NOUPD',
    otherwise set it from `main_log.isatty()`.
    A true value causes the root logger to use `cs.upd` for logging.
  * `ansi_mode`: if `None`,
    set it from `main_log.isatty() and not cs.colourise.env_no_color()`,
    which thus honours the `$NO_COLOR` environment variable
    (see https://no-color.org/ for the convention).
    A true value causes the root logger to colour certain logging levels
    using ANSI terminal sequences (currently only if `cs.upd` is used).
  * `trace_mode`: if `None`, set it according to the presence of
    'TRACE' in flags. Otherwise if `trace_mode` is true, set the
    global `loginfo.trace_level` to `loginfo.level`; otherwise it defaults
    to `logging.DEBUG`.
  * `verbose`: if `None`, then if stderr is a tty then the log
    level is `INFO` otherwise `WARNING`. Otherwise, if `verbose` is
    true then the log level is `INFO` otherwise `WARNING`.
  *`LoggingState.apply(self)`*:
  Apply this `LoggingState` to the current logging setup.

  A logging setup arranged for conventional UNIX command line use.

  LogTime is a context manager that logs the elapsed time of the enclosed
  code. After the run, the field .elapsed contains the elapsed time in
  seconds.
  *`LogTime.__init__(self, tag, *args, **kwargs)`*:
  Set up a LogTime.
  Parameters:
  * `tag`: label included at the start of the log entry
  * `args`: optional array; if not empty `args` is applied to
    `tag` with `%`
  * `level`: keyword argument specifying a log level for a
    default log entry, default `logging.INFO`
  * `threshold`: keyword argument specifying minimum time to
    cause a log, default None (no minimum)
  * `warning_level`: keyword argument specifying the log level
    for a warning log entry, default `logging.WARNING`
  * `warning_threshold`: keyword argument specifying a time
    which raises the log level to `warning_level`

  Add a `FileHandler` logging to the specified `filename`;
  return the chosen logger and the new handler.
  Parameters:
  * `logger`: if supplied and not `None`, add the `FileHandler` to that
    `Logger`, otherwise to the root Logger. If `logger` is a string, call
    `logging.getLogger(logger)` to obtain the logger.
  * `mode`, `encoding` and `delay`: passed to the `FileHandler`
    initialiser.
  * `format`: used to override the handler's default format.
  * `no_prefix`: if true, do not put the `Pfx` context onto the front of the message.

  A `Handler` which discards its requests.
  *`NullHandler.emit(self, record)`*:
  Discard the log record.

  A Formatter subclass that has access to the program's `cmd` and `Pfx` state.
  *`PfxFormatter.__init__(self, fmt=None, datefmt=None, cmd=None)`*:
  Initialise the `PfxFormatter`.
  Parameters:
  * `fmt`: format template,
    default from `DEFAULT_PFX_FORMAT` `'s s s: s'`.
    Passed through to `Formatter.__init__`.
  * `datefmt`:
    Passed through to `Formatter.__init__`.
  * `cmd`: the "command prefix" made available to format strings.
    If not set, `cs.pfx.cmd` is presented.
  *`PfxFormatter.format(self, record)`*:
  Set `record.cmd` and `record.pfx`
  to the global `cmd` and `Pfx` context prefix respectively,
  then call `Formatter.format`.
  *`PfxFormatter.patch_formatter(formatter)`*:
  Monkey patch an existing `Formatter` instance
  with a `format` method which prepends the current `Pfx` prefix.

  Emit a log at `QUIET` level with the current `Pfx` prefix.

  Prepare a `LoggingState` and return it.
  It is also available as the global `cs.logutils.loginfo`.
  Side-effect: sets `cs.pfx.cmd` to this value.

  Emit a log at `STATUS` level with the current `Pfx` prefix.

  Emit a log message at `loginfo.trace_level` with the current `Pfx` prefix.

  Emit a log at `TRACK` level with the current `Pfx` prefix.

  If we're using an `UpdHandler`,
  update the status line otherwise write an info message.
  Note that this calls `Upd.out` directly with `msg%args`
  and thus does not include the current `Pfx` prefix.
  You may well want to use the `status()` function instead.

  A `StreamHandler` subclass whose `.emit` method
  uses a `cs.upd.Upd` for transcription.
  *`UpdHandler.__init__(self, strm=None, upd_level=None, ansi_mode=None, over_handler=None)`*:
  Initialise the `UpdHandler`.
  Parameters:
  * `strm`: the output stream, default `sys.stderr`.
  * `upd_level`: the magic logging level which updates the status line
    via `Upd`. Default: `STATUS`.
  * `ansi_mode`: if `None`, set from `strm.isatty()`.
    A true value causes the handler to colour certain logging levels
    using ANSI terminal sequences.
  *`UpdHandler.emit(self, logrec)`*:
  Emit a `LogRecord` `logrec`.
  For the log level `self.upd_level` update the status line.
  For other levels write a distinct line
  to the output stream, possibly colourised.
  *`UpdHandler.flush(self)`*:
  Flush the update status.

  Emit a log at `VERBOSE` level with the current `Pfx` prefix.

  Emit a log at `logging.WARNING` level with the current `Pfx` prefix.

  Context manager to add a `Logger` to the output logs temporarily.

  *Release 20241109*:
  * setup_logging: make it work with no arguments.
  * ifdebug: handle loginfo.level=None.
  *Release 20241007*:
  * setup_logging: just amend the existing loginfo if already set up.
  * Remove the cs.pfx.cmd side effect from LogState.__init__, now in setup_logging.
  *Release 20240923*:
  setup_logging: accept leading `cmd_name` for backwards compatibility, reported by Lance Cohen.
  *Release 20240630*:
  * New LoggingState class for the computed log state, split out setup_logging() as a little stub.
  * Drop func_wrap and _ftrace, superceded by cs.debug.trace.
  * infer_logging_level: ignore the module.name and module:function_name $DEBUG values, now done by importing cs.debug.
  *Release 20230212*:
  Late import of cs.upd at need to avoid import loop.
  *Release 20220531*:
  PfxFormatter.patch_formatter: notice if record.args is not a tuple and do not try to prefix it (for now).
  *Release 20220530*:
  * New QUIET log level between TRACK and STATUS, add new quiet() logging call.
  * PfxFormatter.patch_formatter: bugfix handling of record.msg,record.args.
  *Release 20220315*:
  A bit of a hack to prevent double patching a formatter, as when BaseCommand calls a BaseCommand and other circumstances where setup_logging() gets called more than once.
  *Release 20220227*:
  * PfxFormatter: new patch_formatter() static method to modify an existing Formatter.
  * setup_logging: just use PfxFormatter.patch_formatter on the first handler's formatter if logging is already set up.
  *Release 20211208*:
  Docstring update.
  *Release 20210721*:
  UpdHandler.emit: for newline-emitting messages, fall back to new .over_handler if the Upd is disabled.
  *Release 20210718*:
  setup_logging: new supplant_root_logger=False parameter to pop the existing handler, typical use supplant_root_logger=sys.stderr.isatty().
  *Release 20210306*:
  * Default logging level for ttys is now INFO, not STATUS.
  * New VERBOSE level below INFO but above DEBUG.
  * infer_logging_level: if verbose unspecified, logging=WARNING on a tty and TRACK otherwise, else if verbose, level=VERBOSE, otherwise WARNING.
  * Include .verbose in the loginfo.
  * New verbose() and ifverbose().
  *Release 20201021*:
  * setup_logging: always provide loginfo.upd, being either main_handler.upd if upd_mode otherwise Upd().
  * exception(): plumb keyword arguments.
  *Release 20200729*:
  setup_logging: honour $NO_COLOR if ansi_mode not specified, per https://no-color.org/
  *Release 20200613*:
  * LogTime: set .end on exit.
  * UpdHandle.emit: fix message colouring logic.
  *Release 20200521*:
  setup_logging: include the logger in loginfo (presently always the root logger).
  *Release 20200519*:
  bugfix setup_logging: apparently a LoggingProxy does not have an encoding
  *Release 20200518*:
  * Sweeping removal of cs.obj.O, universally supplanted by types.SimpleNamespace.
  * Default to logging level TRACK if stderr is a tty instead of logging.INFO.
  * New ifverbose function with leading `verbose` parameter: if None, log at INFO otherwise if true, log at TRACK, otherwise do not log.
  * BREAKING: remove global logging_level and trace_level variables, put it all in the global loginfo.
  * Make STATUS just below TRACK so that it is above INFO instead of below.
  * New status() function for cs.upd messages.
  * UpdHandler: treat status_level as special, going directly to Upd.out.
  * Improved source line recitation on modern Python.
  * Default level if sys.stderr.isatty() now STATUS, not TRACK.
  * Some fixes for loginfo initialisation and setting cs.pfx.cmd.
  *Release 20200229*:
  * Update for new Upd.without context manager.
  * setup_logging: default `upd_mode` to `main_log.isatty()`, was previously False.
  * Drop UpdHandler.upd method, shadowed by instance attribute, never used.
  *Release 20190923*:
  * New `TRACK` constant equal to `logging.INFO+5` to provide a level higher than `INFO`
  * (which seems unreasonably noisy) and lower than `WARNING`
  * warning for tracking salient events.
  * New `track()` function to match.
  *Release 20190220*:
  Improvements to upd_mode.
  *Release 20190103*:
  Documentation updates.
  *Release 20190101*:
  Bugfix for @contextmanager usage.
  *Release 20171030*:
  Assorted fixes from recent module reshuffle. Other small features and cleanups. Drop a couple of unused functions.
  *Release 20160828*:
  Use "install_requires" instead of "requires" in DISTINFO.
  *Release 20160827*:
  * Pfx: import __exit__ handler
  * Preliminary per-module and per-function syntax accepted in $DEBUG envvar.
  * Improvements to X(), add DP() and XP() prefixed flavours.
  * status() function to update terminal status line.
  * New X_via_tty global flag: directs X() to tty instead of sys.stderr.
  * Assorted other minor improvements.
  *Release 20150118*:
  metadata updates
  *Release 20150110*:
  Initial PyPI release.


pkgId: 4dec40344ed465dda0d564299e184250fe029aafd9120f429876a62858537e36
name: python-cs.obj
arch: src
version: 20241009
epoch: 0
release: 1
summary: Convenience facilities for objects.
url: https://pypi.org/project/cs.obj/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.obj-20241009-1.src.rpm
checksum_type: sha256
description:
  Convenience facilities for objects.

  *Latest release 20241009*:
  public_subclasses: catch TypeError from "type.__subclasses__", which is just a function.



  Return a dictionary with keys mapping to the values of the attributes of `o`.

  Parameters:
  * `o`: the object to map
  * `selector`: the optional selection criterion

  If `selector` is omitted or `None`, select "public" attributes,
  those not commencing with an underscore.

  If `selector` is a `str`, select attributes starting with `selector`.

  Otherwise presume `selector` is callable
  and select attributes `attr` where `selector(attr)` is true.



  Convenient function to shallow copy an object with simple modifications.

  Performs a shallow copy of `self` using copy.copy.

  Treat all positional parameters as attribute names, and
  replace those attributes with shallow copies of the original
  attribute.

  Treat all keyword arguments as (attribute,value) tuples and
  replace those attributes with the supplied values.



  Return constants indicating the ``flavour'' of an object:
  * `T_MAP`: DictType, DictionaryType, objects with an __keys__ or keys attribute.
  * `T_SEQ`: TupleType, ListType, objects with an __iter__ attribute.
  * `T_SCALAR`: Anything else.



  The `O` class is now obsolete, please subclass `types.SimpleNamespace`
  or use a dataclass.



  Generator yielding `(attr,value)` for relevant attributes of `o`.



  Yield attribute names from `o` which are pertinent to `O_str`.

  Note: this calls `getattr(o,attr)` to inspect it in order to
  prune callables.



  Merge key:value pairs from a mapping into an object.

  Ignore keys that do not start with a letter.
  New attributes or attributes whose values compare equal are
  merged in. Unequal values are passed to:

      _conflict(o, attr, old_value, new_value)

  to resolve the conflict. If _conflict is omitted or None
  then the new value overwrites the old if _overwrite is true.



  Return a `str` representation of the object `o`.

  Parameters:
  * `o`: the object to describe.
  * `no_recurse`: if true, do not recurse into the object's structure.
    Default: `False`.
  * `seen`: a set of previously sighted objects
    to prevent recursion loops.



  OBSOLETE obj_as_dict

  OBSOLETE convesion of an object to a `dict`. Please us `cs.obj.as_dict`.



  An extremely simple proxy object
  that passes all unmatched attribute accesses to the proxied object.

  Note that setattr and delattr work directly on the proxy, not the proxied object.



  Return a list of the subclasses of `cls` which has public names.



  A simple class for named sentinels whose `str()` is just the name
  and whose `==` uses `is`.

  Example:

      >>> from cs.obj import Sentinel
      >>> MISSING = Sentinel("MISSING")
      >>> print(MISSING)
      MISSING
      >>> other = Sentinel("other")
      >>> MISSING == other
      False
      >>> MISSING == MISSING
      True



  Obtain an object for `key` via `registry` (a mapping of `key`=>object).
  Return `(is_new,object)`.

  If the `key` exists in the registry, return the associated object.
  Otherwise create a new object by calling `factory(*fargs,**fkwargs)`
  and store it as `key` in the `registry`.

  The `registry` may be any mapping of `key`s to objects
  but will usually be a `weakref.WeakValueDictionary`
  in order that object references expire as normal,
  allowing garbage collection.

  *Note*: this function *is not* thread safe.
  Multithreaded users should hold a mutex.

  See the `SingletonMixin` class for a simple mixin to create
  singleton classes,
  which does provide thread safe operations.



  A mixin turning a subclass into a singleton factory.

  *Note*: this mixin overrides `object.__new__`
  and may not play well with other classes which override `__new__`.

  *Warning*: because of the mechanics of `__new__`,
  the instance's `__init__` method will always be called
  after `__new__`,
  even when a preexisting object is returned.
  Therefore that method should be sensible
  even for an already initialised
  and probably subsequently modified object.

  My suggested approach is to access some attribute,
  and preemptively return if it already exists.
  Example:

      def __init__(self, x, y):
          if 'x' in self.__dict__:
              return
          self.x = x
          self.y = y

  *Note*: we probe `self.__dict__` above to accomodate classes
  with a `__getattr__` method.

  *Note*: each class registry has a lock,
  which ensures that reuse of an object
  in multiple threads will call the `__init__` method
  in a thread safe serialised fashion.

  Implementation requirements:
  a subclass should:
  * provide a method `_singleton_key(*args,**kwargs)`
    returning a key for use in the single registry,
    computed from the positional and keyword arguments
    supplied on instance creation
    i.e. those which `__init__` would normally receive.
    This should have the same signature as `__init__`
    but using `cls` instead of `self`.
  * provide a normal `__init__` method
    which can be safely called again
    after some earlier initialisation.

  This class is thread safe for the registry operations.

  Example:

      class Pool(SingletonMixin):

          @classmethod
          def _singleton_key(cls, foo, bah=3):
              return foo, bah

          def __init__(self, foo, bah=3):
              if hasattr(self, 'foo'):
                  return
             ... normal __init__ stuff here ...
             self.foo = foo
             ...

  *`SingletonMixin.__hash__(self)`*:
  default hash and equality methods

  *`SingletonMixin.singleton_also_by(also_key, key)`*:
  Obtain a singleton by a secondary key.
  Return the instance or `None`.

  Parameters:
  * `also_key`: the name of the secondary key index
  * `key`: the key for the index



  A mixin to track all instances of a particular class.

  This is aimed at checking the global state of objects of a
  particular type, particularly states like counters. The
  tracking is attached to the class itself.

  The class to be tracked includes this mixin as a superclass and calls:

      TrackedClassMixin.__init__(class_to_track)

  from its __init__ method. Note that `class_to_track` is
  typically the class name itself, not `type(self)` which would
  track the specific subclass. At some relevant point one can call:

      self.tcm_dump(class_to_track[, file])

  `class_to_track` needs a `tcm_get_state` method to return the
  salient information, such as this from cs.resources.MultiOpenMixin:

      def tcm_get_state(self):
          return {'opened': self.opened, 'opens': self._opens}

  See cs.resources.MultiOpenMixin for example use.

  *`TrackedClassMixin.tcm_all_state(klass)`*:
  Generator yielding tracking information
  for objects of type `klass`
  in the form `(o,state)`
  where `o` if a tracked object
  and `state` is the object's `get_tcm_state` method result.

  *`TrackedClassMixin.tcm_dump(klass, f=None)`*:
  Dump the tracking information for `klass` to the file `f`
  (default `sys.stderr`).





  *Release 20241009*:
  public_subclasses: catch TypeError from "type.__subclasses__", which is just a function.

  *Release 20241005*:
  New public_subclasses(cls) returning all subclasses with public names.

  *Release 20220918*:
  * SingletonMixin: change example to probe self__dict__ instead of hasattr, faster and less fragile.
  * New Sentinel class for named sentinel objects, equal only to their own instance.

  *Release 20220530*:
  SingletonMixin: add default __hash__ and __eq__ methods to support dict and set membership.

  *Release 20210717*:
  SingletonMixin: if cls._singleton_key returns None we always make a new instance and do not register it.

  *Release 20210306*:
  SingletonMixin: make singleton_also_by() a public method.

  *Release 20210131*:
  SingletonMixin: new _singleton_also_indexmap method to return a mapping of secondary keys to values to secondary lookup, _singleton_also_index() to update these indices, _singleton_also_by to look up a secondary index.

  *Release 20210122*:
  SingletonMixin: new _singleton_instances() method returning a list of the current instances.

  *Release 20201227*:
  SingletonMixin: correctly invoke __new__, a surprisingly fiddly task to get right.

  *Release 20201021*:
  * @OBSOLETE(obj_as_dict), recommend "as_dict()".
  * [BREAKING] change as_dict() to accept a single optional selector instead of various mutually exclusive keywords.

  *Release 20200716*:
  SingletonMixin: no longer require special _singleton_init method, reuse default __init__ implicitly through __new__ mechanics.

  *Release 20200517*:
  Documentation improvements.

  *Release 20200318*:
  * Replace obsolete O class with a new subclass of SimpleNamespace which issues a warning.
  * New singleton() generic factory function and SingletonMixin mixin class for making singleton classes.

  *Release 20190103*:
  * New mixin class TrackedClassMixin to track all instances of a particular class.
  * Documentation updates.

  *Release 20170904*:
  Minor cleanups.

  *Release 20160828*:
  * Use "install_requires" instead of "requires" in DISTINFO.
  * Minor tweaks.

  *Release 20150118*:
  move long_description into cs/README-obj.rst

  *Release 20150110*:
  cleaned out some old junk, readied metadata for PyPI


pkgId: 85e0f027c4aabbe7bcd7bb7c01d5b22823e71f288067c9707aa229497bcb7143
name: python-cs.progress
arch: src
version: 20240412
epoch: 0
release: 1
url: https://pypi.org/project/cs.progress/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.progress-20240412-1.src.rpm
checksum_type: sha256
summary:
  A progress tracker with methods for throughput, ETA and update notification; also a compound progress meter composed from other progress meters.

description:
  A progress tracker with methods for throughput, ETA and update notification;
  also a compound progress meter composed from other progress meters.

  *Latest release 20240412*:
  * BaseProgress.status: fixes for the arrow_width computation.
  * BaseProgress.bar: drop existing UpdProxy support, drop deferred, implement update_period using a ticker Thread.
  * BaseProgress.bar: set update_period=DEFAULT_UPDATE_PERIOD by default.
  * Progress.iterbar: drop preexisting UpdProxy support, update_frequency and update_min_size support.
  * Progress: new advance_total(delta) method so that we have a callable for this.
  * BaseProgress.bar: new optional poll parameter accepting a callable accepting a BaseProgress to update the state before updating the bar display.
  * BaseProgress.bar: new stalled='stalled' parameter to specify the term for no recent throughput, workers might prefer 'idle'.
  * progressbar() updated to match.



  Decorator for a function accepting an optional `progress`
  keyword parameter.
  If `progress` is `None` and the default `Upd` is not disabled,
  run the function with a progress bar.



  The base class for `Progress` and `OverProcess`
  with various common methods.

  Note that durations are in seconds
  and that absolute time is in seconds since the UNIX epoch
  (the basis of `time.time()`).

  *Method `BaseProgress.__init__(self, name=None, start_time=None, units_scale=None)`*:
  Initialise a progress instance.

  Parameters:
  * `name`: optional name
  * `start_time`: optional UNIX epoch start time, default from `time.time()`
  * `units_scale`: a scale for use with `cs.units.transcribe`,
    default `BINARY_BYTES_SCALE`

  *Method `BaseProgress.__eq__(self, other)`*:
  A Progress is equal to another object `other`
  if its position equals `int(other)`.

  *Method `BaseProgress.__ge__(self, other, NotImplemented=NotImplemented)`*:
  Return a >= b.  Computed by @total_ordering from (not a < b).

  *Method `BaseProgress.__gt__(self, other, NotImplemented=NotImplemented)`*:
  Return a > b.  Computed by @total_ordering from (not a < b) and (a != b).

  *Method `BaseProgress.__int__(self)`*:
  `int(Progress)` returns the current position.

  *Method `BaseProgress.__le__(self, other, NotImplemented=NotImplemented)`*:
  Return a <= b.  Computed by @total_ordering from (a < b) or (a == b).

  *Method `BaseProgress.__lt__(self, other)`*:
  A Progress is less then another object `other`
  if its position is less than `int(other)`.

  *Method `BaseProgress.arrow(self, width, no_padding=False)`*:
  Construct a progress arrow representing completion
  to fit in the specified `width`.

  *Method `BaseProgress.bar(*a, upd: Optional[cs.upd.Upd] = <function uses_upd.<locals>.<lambda> at 0x110fbd6c0>, **kw)`*:
  A context manager to create and withdraw a progress bar.
  It returns the `UpdProxy` which displays the progress bar.

  Parameters:
  * `label`: a label for the progress bar,
    default from `self.name`.
  * `statusfunc`: an optional function to compute the progress bar text
    accepting `(self,label,width)`.
  * `width`: an optional width expressing how wide the progress bar
    text may be.
    The default comes from the `proxy.width` property.
  * `recent_window`: optional timeframe to define "recent" in seconds;
    if the default `statusfunc` (`Progress.status`) is used
    this is passed to it
  * `report_print`: optional `print` compatible function
    with which to write a report on completion;
    this may also be a `bool`, which if true will use `Upd.print`
    in order to interoperate with `Upd`.
  * `stalled`: optional string to replace the word `'stalled'`
    in the status line; for a worked this might be betteer as `'idle'`
  * `insert_pos`: where to insert the progress bar, default `1`
  * `poll`: an optional callable accepting a `BaseProgress`
    which can be used to update the progress state before
    updating the progress bar display

  Example use:

      
      
      
      P = Progress(name=label)
      with P.bar(report_print=True):
          upload_filename(src, progress=P)

  *Property `BaseProgress.elapsed_time`*:
  Time elapsed since `start_time`.

  *Property `BaseProgress.eta`*:
  The projected time of completion: now + `remaining_time`.

  If `remaining_time` is `None`, this is also `None`.

  *Method `BaseProgress.format_counter(self, value, scale=None, max_parts=2, sep=',', **kw)`*:
  Format `value` accoridng to `scale` and `max_parts`
  using `cs.units.transcribe`.

  *Method `BaseProgress.iterbar(self, it, label=None, *, itemlenfunc=None, incfirst=False, update_period=0.3, **bar_kw)`*:
  An iterable progress bar: a generator yielding values
  from the iterable `it` while updating a progress bar.

  Parameters:
  * `it`: the iterable to consume and yield.
  * `label`: a label for the progress bar,
    default from `self.name`.
  * `itemlenfunc`: an optional function returning the "size" of each item
    from `it`, used to advance `self.position`.
    The default is to assume a size of `1`.
    A convenient alternative choice may be the builtin function `len`.
  * `incfirst`: whether to advance `self.position` before we
    `yield` an item from `it` or afterwards.
    This reflects whether it is considered that progress is
    made as items are obtained or only after items are processed
    by whatever is consuming this generator.
    The default is `False`, advancing after processing.
  * `update_period`: default `DEFAULT_UPDATE_PERIOD`; if `0`
    then update on every iteration, otherwise every `update_period`
    seconds
  Other parameters are passed to `Progress.bar`.

  Example use:

      from cs.units import DECIMAL_SCALE
      rows = [some list of data]
      P = Progress(total=len(rows), units_scale=DECIMAL_SCALE)
      for row in P.iterbar(rows, incfirst=True):
          ... do something with each row ...

      f = open(data_filename, 'rb')
      datalen = os.stat(f).st_size
      def readfrom(f):
          while True:
              bs = f.read(65536)
              if not bs:
                  break
              yield bs
      P = Progress(total=datalen)
      for bs in P.iterbar(readfrom(f), itemlenfunc=len):
          ... process the file data in bs ...

  *Property `BaseProgress.ratio`*:
  The fraction of progress completed: `(position-start)/(total-start)`.
  Returns `None` if `total` is `None` or `total<=start`.

  Example:

      >>> P = Progress()
       P.ratio
      >>> P.total = 16
      >>> P.ratio
      0.0
      >>> P.update(4)
      >>> P.ratio
      0.25

  *Property `BaseProgress.remaining_time`*:
  The projected time remaining to end
  based on the `throughput` and `total`.

  If `total` is `None`, this is `None`.

  *Method `BaseProgress.status(self, label, width, recent_window=None, stalled=None)`*:
  A progress string of the form:
  *label*`: `*pos*`/`*total*` ==>  ETA '*time*

  Parameters:
  * `label`: the label for the status line;
    if `None` use `self.name`
  * `width`: the available width for the status line;
    if not an `int` use `width.width`
  * `recent_window`: optional timeframe to define "recent" in seconds,
    default : `5`
  * `stalled`: the label to indicate no throughput, default `'stalled'`;
    for a worker this might often b better as `'idle'`

  *Method `BaseProgress.text_pos_of_total(self, fmt=None, fmt_pos=None, fmt_total=None, pos_first=False)`*:
  Return a "total:position" or "position/total" style progress string.

  Parameters:
  * `fmt`: format string interpolating `pos_text` and `total_text`.
    Default: `"{pos_text}/{total_text}"` if `pos_first`,
    otherwise `"{total_text}:{pos_text}"`
  * `fmt_pos`: formatting function for `self.position`,
    default `self.format_counter`
  * `fmt_total`: formatting function for `self.total`,
    default from `fmt_pos`
  * `pos_first`: put the position first if true (default `False`),
    only consulted if `fmt` is `None`

  *Property `BaseProgress.throughput`*:
  The overall throughput: `self.throughput_overall()`.

  By comparison,
  the `Progress.throughput` property is `self.throughput_recent`
  if the `throughput_window` is not `None`,
  otherwise it falls back to `throughput_overall`.

  *Method `BaseProgress.throughput_overall(self)`*:
  The overall throughput from `start` to `position`
  during `elapsed_time`.

  *Method `BaseProgress.throughput_recent(self, time_window)`*:
  The recent throughput. Implemented by subclasses.



  CheckPoint(time, position)

  *Property `CheckPoint.position`*:
  Alias for field number 1

  *Property `CheckPoint.time`*:
  Alias for field number 0



  A `Progress`-like class computed from a set of subsidiary `Progress`es.

  AN OverProgress instance has an attribute ``notify_update`` which
  is a set of callables.
  Whenever the position of a subsidiary `Progress` is updated,
  each of these will be called with the `Progress` instance and `None`.

  Example:

      >>> P = OverProgress(name="over")
      >>> P1 = Progress(name="progress1", position=12)
      >>> P1.total = 100
      >>> P1.advance(7)
      >>> P2 = Progress(name="progress2", position=20)
      >>> P2.total = 50
      >>> P2.advance(9)
      >>> P.add(P1)
      >>> P.add(P2)
      >>> P1.total
      100
      >>> P2.total
      50
      >>> P.total
      150
      >>> P1.start
      12
      >>> P2.start
      20
      >>> P.start
      0
      >>> P1.position
      19
      >>> P2.position
      29
      >>> P.position
      16

  *Method `OverProgress.add(self, subprogress)`*:
  Add a subsidairy `Progress` to the contributing set.

  *Property `OverProgress.eta`*:
  The `eta` is the maximum of the subsidiary etas.

  *Property `OverProgress.position`*:
  The `position` is the sum off the subsidiary position offsets
  from their respective starts.

  *Method `OverProgress.remove(self, subprogress, accrue=False)`*:
  Remove a subsidairy `Progress` from the contributing set.

  *Property `OverProgress.start`*:
  We always return a starting value of 0.

  *Property `OverProgress.throughput`*:
  The `throughput` is the sum of the subsidiary throughputs.

  *Method `OverProgress.throughput_recent(self, time_window)`*:
  The `throughput_recent` is the sum of the subsidiary throughput_recentss.

  *Property `OverProgress.total`*:
  The `total` is the sum of the subsidiary totals.



  A progress counter to track task completion with various utility methods.

  Example:

      >>> P = Progress(name="example")
      >>> P                         #doctest: +ELLIPSIS
      Progress(name='example',start=0,position=0,start_time=...,throughput_window=None,total=None):[CheckPoint(time=..., position=0)]
      >>> P.advance(5)
      >>> P                         #doctest: +ELLIPSIS
      Progress(name='example',start=0,position=5,start_time=...,throughput_window=None,total=None):[CheckPoint(time=..., position=0), CheckPoint(time=..., position=5)]
      >>> P.total = 100
      >>> P                         #doctest: +ELLIPSIS
      Progress(name='example',start=0,position=5,start_time=...,throughput_window=None,total=100):[CheckPoint(time=..., position=0), CheckPoint(time=..., position=5)]

  A Progress instance has an attribute ``notify_update`` which
  is a set of callables. Whenever the position is updated, each
  of these will be called with the `Progress` instance and the
  latest `CheckPoint`.

  `Progress` objects also make a small pretense of being an integer.
  The expression `int(progress)` returns the current position,
  and `+=` and `-=` adjust the position.

  This is convenient for coding, but importantly it is also
  useful for discretionary use of a Progress with some other
  object.
  If you want to make a lightweight `Progress` capable class
  you can set a position attribute to an `int`
  and manipulate it carefully using `+=` and `-=` entirely.
  If you decide to incur the cost of maintaining a `Progress` object
  you can slot it in:

      
      my_thing.amount = 0

      
      my_thing.amount = Progress(my_thing.amount)

  *Method `Progress.__init__(self, name: Optional[str] = None, *, position: Optional[float] = None, start: Optional[float] = None, start_time: Optional[float] = None, throughput_window: Optional[int] = None, total: Optional[float] = None, units_scale=None)`*:
  Initialise the Progesss object.

  Parameters:
  * `position`: initial position, default `0`.
  * `name`: optional name for this instance.
  * `start`: starting position of progress range,
    default from `position`.
  * `start_time`: start time of the process, default now.
  * `throughput_window`: length of throughput time window in seconds,
    default None.
  * `total`: expected completion value, default None.

  *Method `Progress.__iadd__(self, delta)`*:
  Operator += form of advance().

  >>> P = Progress()
  >>> P.position
  0
  >>> P += 4
  >>> P.position
  4
  >>> P += 4
  >>> P.position
  8

  *Method `Progress.__isub__(self, delta)`*:
  Operator -= form of advance().

  >>> P = Progress()
  >>> P.position
  0
  >>> P += 4
  >>> P.position
  4
  >>> P -= 4
  >>> P.position
  0

  *Method `Progress.advance(self, delta, update_time=None)`*:
  Record more progress, return the advanced position.

  >>> P = Progress()
  >>> P.position
  0
  >>> P.advance(4)
  >>> P.position
  4
  >>> P.advance(4)
  >>> P.position
  8

  *Method `Progress.advance_total(self, delta)`*:
  Function form of addition to the total.

  *Property `Progress.latest`*:
  Latest datum.

  *Property `Progress.position`*:
  Latest position.

  *Property `Progress.throughput`*:
  Current throughput per second.

  If `self.throughput_window` is not `None`,
  calls `self.throughput_recent(throughput_window)`.
  Otherwise call `self.throughput_overall()`.

  *Method `Progress.throughput_recent(self, time_window)`*:
  Recent throughput per second within a time window in seconds.

  The time span overlapping the start of the window is included
  on a flat pro rata basis.

  *Property `Progress.total`*:
  Return the current total.

  *Method `Progress.update(self, new_position, update_time=None)`*:
  Record more progress.

  >>> P = Progress()
  >>> P.position
  0
  >>> P.update(12)
  >>> P.position
  12



  Convenience function to construct and run a `Progress.iterbar`
  wrapping the iterable `it`,
  issuing and withdrawning a progress bar during the iteration.

  Parameters:
  * `it`: the iterable to consume
  * `label`: optional label, doubles as the `Progress.name`
  * `position`: optional starting position
  * `total`: optional value for `Progress.total`,
    default from `len(it)` if supported.
  * `units_scale`: optional units scale for `Progress`,
    default `UNSCALED_SCALE`

  If `total` is `None` and `it` supports `len()`
  then the `Progress.total` is set from it.

  All arguments are passed through to `Progress.iterbar`.

  Example use:

      for row in progressbar(rows):
          ... do something with row ...



  Exercise some of the functionality.





  *Release 20240412*:
  * BaseProgress.status: fixes for the arrow_width computation.
  * BaseProgress.bar: drop existing UpdProxy support, drop deferred, implement update_period using a ticker Thread.
  * BaseProgress.bar: set update_period=DEFAULT_UPDATE_PERIOD by default.
  * Progress.iterbar: drop preexisting UpdProxy support, update_frequency and update_min_size support.
  * Progress: new advance_total(delta) method so that we have a callable for this.
  * BaseProgress.bar: new optional poll parameter accepting a callable accepting a BaseProgress to update the state before updating the bar display.
  * BaseProgress.bar: new stalled='stalled' parameter to specify the term for no recent throughput, workers might prefer 'idle'.
  * progressbar() updated to match.

  *Release 20230401*:
  progressbar, BaseProgress.iterbar: use @uses_upd to provide a context Upd instance.

  *Release 20230212*:
  BaseProgress: new update_period=0.2 parameter to constraint updates by elapsed time.

  *Release 20221207*:
  * BaseProgress.format_counter: accept arbitrary keyword arguments to pass to cs.units.transcribe.
  * Progress.__init__: accept floats instead of just ints.

  *Release 20220918*:
  Progress.iterbar: wrap the iteration in a try/finally for cleanup.

  *Release 20211208*:
  * Progress.__init__: make the first optional positional parameter be "name", make other parameters keyword only.
  * Progress.bar: make "label" the first optional positional parameter, make others keyword only.

  *Release 20210803*:
  * progressbar,iterbar: accept optional RunState to cancel iteration.
  * BaseProgress.iterbar: make update_min_size properly optional, was making update_frequency ineffective.

  *Release 20210730*:
  When there is no total just report position and no ETA.

  *Release 20210717*:
  Minor tweaks.

  *Release 20210316*:
  * Progress.iterbar: only update the status line once per iteration, either before or after the yield according to incfirst.
  * Progress.iterbar: fix the meaning of update_frequency to count iterations, add update_min_size to count progress advance.

  *Release 20210306*:
  progressbar: accept new optional `position` parameter, used to initialise the Progress.

  *Release 20201102.1*:
  DISTINFO: fix module dependencies.

  *Release 20201102*:
  * Format/layout changes for the default status line.
  * Progress.throughtput_recent: return None if no new positions beyond the starting position.
  * BaseProgress.status: accept label=None (default to self.name) and width=UpdProxy (uses width.width).
  * BaseProgress.status: new optional window parameter, default 5, defining the recent throughput window size in seconds.
  * A few bugfixes.

  *Release 20201025*:
  * Some formatting improvements.
  * BaseProgress.bar: new insert_pos parameter to position the progress bar, default still 1.
  * BaseProgress.bar: new deferred parameter putting off the status bar until the first update.
  * BaseProgress.bar: accept new optional `proxy` parameter to use (and not delete) an existing UpdProxy for display.
  * Progress.text_pos_of_total: new `pos_first=False` parameter, rendering the total before the position by default (less progress bar noise).
  * New @auto_progressbar decorator to provide a progress bar and initialise progress= parameter to functions which can use a Progress for reporting.
  * Assorted fixes.

  *Release 20200718.3*:
  BaseProgress.bar, progressbar: new optional report_print parameter for reporting on completion.

  *Release 20200718.2*:
  Bugfix: BaseProgress.status: handle throughput=0 when total=None.

  *Release 20200718.1*:
  BaseProgress.bar, progressbar: new optional update_frequency parameter for less frequent updates.

  *Release 20200718*:
  * Readability improvement for default status line.
  * progressbar: default units_scale=UNSCALED_SCALE.

  *Release 20200716.1*:
  BaseProgress.status: round throughput to an int if >=10.

  *Release 20200716*:
  * BaseProgress.status: distinguish "idle" (position >= total) from "stalled" (position < total).
  * BaseProgress.status: make the status very short if the progress is idle.

  *Release 20200627*:
  * BaseProgress.status: handle throughput=None (before any activity).
  * BaseProgress: drop count_of_total_bytes_text, superceded by format_counter (which honours the units_scale).

  *Release 20200626*:
  * New Progress.bar generator method iterating over an iterable while displaying a progress bar.
  * New convenience function progressbar(it,...) which rolls its own Progress instance.
  * Progress: always support a throughput window, default to DEFAULT_THROUGHPUT_WINDOW = 5s.
  * Improve the default progress bar render returned by Progress.status().

  *Release 20200613*:
  * BaseProgress, Progress and OverProgress now accept an optional units_scale, such as cs.units.UNSCALED_SCALE, to use when expressing progress - the default remains BINARY_SCALE.
  * New arrow(), format_counter() and text_pos_of_total() methods to produce components of the status string for tuning or external reuse.

  *Release 20200520*:
  OverProgress: throughput and eta implementations.

  *Release 20200129.3*:
  Test __version__ machinery again.

  *Release 20200129.2*:
  set __version__ to '20200129.2'

  *Release 20200129.1*:
  Dummy release to test new __version__.

  *Release 20200129*:
  New Progress.count_of_total_bytes_text property presenting "3kB/40MB" style text.

  *Release 20190812*:
  * New OverProgress class which is a composite of a set of subsidiary Progress instances.
  * Assorted other small updates.

  *Release 20190220*:
  * Progress: be somewhat like an int.
  * New status() method returning a convenient one line progress status report.

  *Release 20180703.2*:
  Progress: make .total into a property in order to fire the update notifications.

  *Release 20180703.1*:
  Progress: additions and changes to API: new .ratio, .elapsed_time, rename .projected to .remaining_time.

  *Release 20180703*:
  Initial release of cs.progress.


pkgId: de47536299d4b96a510dbcf633918ded63ec5311bfcda93a32c190e037a9994b
name: python-cs.py.func
arch: src
version: 20240630
epoch: 0
release: 1
summary: Convenience facilities related to Python functions.
url: https://pypi.org/project/cs.py.func/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.py.func-20240630-1.src.rpm
checksum_type: sha256
description:
  Convenience facilities related to Python functions.

  *Latest release 20240630*:
  funcname: include the module name, handle functions with no __module__.



  Call `func(*a,**kw)` if `doit` is true
  otherwise just print it out.

  The parameter `func` may be preceeded optionally by a `dict`
  containing modes. The current modes are:
  * `'print'`: the print function, default the builtin `print`



  Call the named `method` on the object `o` if it exists.

  If it does not exist, return `default` (which defaults to None).
  Otherwise call getattr(o, method)(*a, **kw).
  `a` defaults to ().
  `kw` defaults to {}.



  A property which must be recomputed
  if the revision of another property exceeds the snapshot revision.



  A property which must be recomputed
  if the reference revision (attached to self)
  exceeds the snapshot revision.



  Return a string representing a call to `func(*a,**kw)`.



  Prepare a percent-format string and associated argument list
  describing a call to `func(*a,**kw)`.
  Return `format,args`.

  The `func` argument can also be a string,
  typically a prepared description of `func` such as `funccite(func)`.

  *Note*: the returned `args` is a `list` for easy incorporation
  into further arguments.  The `%` operator requires a `tuple`.



  Return a citation for a function (name and code location).



  Return a name for the supplied function `func`.
  Several objects do not have a __name__ attribute, such as partials.



  A substitute for the builtin @property.

  The builtin @property decorator lets internal AttributeErrors escape.
  While that can support properties that appear to exist conditionally,
  in practice this is almost never what I want, and it masks deeper errors.
  Hence this wrapper for @property that transmutes internal AttributeErrors
  into RuntimeErrors.





  *Release 20240630*:
  funcname: include the module name, handle functions with no __module__.

  *Release 20230331*:
  Drop @trace, moved to cs.debug.

  *Release 20230210*:
  Drop the returns_* and yields_* decorators, these days type annotations and @typechecked are the go.

  *Release 20221207*:
  * @trace: trace exceptions by default, previously false.
  * Late import of cs.fs.shortpath to avoid import loop.
  * New func_a_kw(func,*a,**kw) returning the formatted form of func_a_kw_fmt(func,*a,**kw).

  *Release 20221118*:
  @trace: add indenting.

  *Release 20220619*:
  @trace: rename some parameters, add with_caller.

  *Release 20220311.1*:
  New callif(doit[,modes],func,*a,**kw) to call or print a function, used in -n/--dry-run stuff sometimes.

  *Release 20220311*:
  * @trace: new pprint=False option to use pprint.pformat instead of repr for the return value.
  * @trace: bugfix use of retval parameter.

  *Release 20210913*:
  New func_a_kw_fmt(func,*a,**kw) imported from cs.pfx, hooked it into @trace.

  *Release 20210906*:
  funcname: special case functools.partial, return concise name.

  *Release 20210717*:
  * Move @trace from debug to py.func, defer log call imports to avoid loops.
  * Drop cs.pfx requirement, import opportunisticly with fallback.

  *Release 20200518*:
  funcname: prefer `func.__qualname__` over `func.__name__`

  *Release 20190729*:
  funccite: handle callables with no __code__ attribute.

  *Release 20190108*:
  Break import loop. Use cs.py3.raise_from for portability.

  *Release 20181231*:
  * Some type specific convenience wrappers for yields_type and returns_type.
  * Bugfix for @prop.

  *Release 20170906.1*:
  Minor tweaks.

  *Release 20170906*:
  Bugfix for @prop.

  *Release 20170608*:
  New decorator @prop which works just like the builtin @property, but transmutes internal AttributeError into RuntimeError, unmasking many bugs.

  *Release 20160828*:
  Use "install_requires" instead of "requires" in DISTINFO.

  *Release 20150115*:
  First PyPI release.


pkgId: 4d3be83b086cdadaee947b35a895d9e362aff352c136031326a7a6f7c74fb195
name: python-cs.py.stack
arch: src
version: 20240630
epoch: 0
release: 1
summary: Convenience functions for the python execution stack.
url: https://pypi.org/project/cs.py.stack/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.py.stack-20240630-1.src.rpm
checksum_type: sha256
description:
  I find the supplied python traceback facilities quite awkward.
  These functions provide convenient facilities.

  *Latest release 20240630*:
  * The fallback StackSummary: add format_frame_summary(frame) static method.
  * stack_dump: updates to avoid a name conflict, expect to work off a StackSummary or a list of frames.



  Return the `Frame` of the caller's caller.
  Returns `None` if `frame_index` is out of range.

  Useful `frame_index` values:
  * `-1`: caller, this function
  * `-2`: invoker, who wants to know the caller
  * `-3`: the calling function of the invoker

  The default `from_index` value is `-3`.



  Return the current stack as a `StackSummary` instance, a list
  of `FrameSummary` instances.



  Recite current or supplied stack to `f`, default `sys.stderr`.

  Parameters:
  * `f`: the output file object, default `sys.stderr`
  * `indent`: how many spaces to indent the stack lines, default `0`
  * `summary`: the stack `Frame`s to write,
    default obtained from the current stack
  * `skip`: the number of `Frame`s to trim from the end of `summary`;
    if `summary` is `None` this defaults to `2` to trim the `Frame`s
    for the `stack_dump` function and its call to `frames()`,
    otherwise the default is `0` to use the supplied `Frame`s as is
  * `select`: if not `None`, select particular frames;
    if `select` is a `str` it must be present in the frame filename;
    otherwise `select(frame)` must be true





  *Release 20240630*:
  * The fallback StackSummary: add format_frame_summary(frame) static method.
  * stack_dump: updates to avoid a name conflict, expect to work off a StackSummary or a list of frames.

  *Release 20240519*:
  Drop Frame.funcname property.

  *Release 20240412*:
  * stack_dump: new select parameter to pick interesting frames for the listing.
  * Provide StackSummary and FrameSummary if Python too old.

  *Release 20220918*:
  caller(): return None if the frame offset is out of range.

  *Release 20220429*:
  caller: return None if the stack index is out of range.

  *Release 20190812*:
  stack_dump(): trim the last 2 frames from the listing by default (they are part of stack_dump's implementation).

  *Release 20190101*:
  * _Frame: rename .functionname to .funcname; caller: turn raw frames into Frames.
  * caller(): accept optional frame_index, default -3.

  *Release 20160827*:
  Add stack_dump().

  *Release 20150115*:
  PyPI metadata fixups.

  *Release 20150111*:
  Tag for initial PyPI release of cs.py.stack.


pkgId: 2c87f25c7372fa718bdd98bf2150b379e484ae442cc6939e57f24ee6eeb841ce
name: python-cs.py3
arch: src
version: 20220523
epoch: 0
release: 1
summary: Aids for code sharing between python2 and python3.
url: https://bitbucket.org/cameron_simpson/css/commits/all
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.py3-20220523-1.src.rpm
checksum_type: sha256
description:
  Aids for code sharing between python2 and python3.

  *Latest release 20220523*:
  Minor tweak to ustr().

  This package presents various names in python 3 flavour for common use in
  python 2 and python 3.



  Upgrade string to unicode: no-op for python 3.





  *Release 20220523*:
  Minor tweak to ustr().

  *Release 20200517*:
  Add date_fromisoformat and datetime_fromisoformat being the datetime.date and datetime.datetime isoformat factories.

  *Release 20200229*:
  Minor fixes.

  *Release 20190729*:
  Add DEVNULL, which only arrived with 3.3.

  *Release 20190331*:
  cs.py3._for3.raise3: bugfix raise-with-traceback.

  *Release 20190108*:
  New raise_from function to provide raise...from in py3 and plain raise in py2.

  *Release 20181108*:
  Small import fix for pread.

  *Release 20180805*:
  Implement pread for systems lacking os.pread.

  *Release 20170903*:
  * Make into a package subsuming cs.py3_for2 and cs.py3_for3.
  * Implementation of struct.iter_unpack.
  * Make bytes.__eq__ work with str for Python 2.
  * New name joinbytes for Python 2 and 3.
  * Backports for Python 2.5.

  *Release 20160828*:
  Use "install_requires" instead of "requires" in DISTINFO.

  *Release 20160827*:
  * Move python 2 and 3 specific code into cs.py3_for2 and cs.py3_for3.
  * Do not bother with StringIO and BytesIO, modules can get them directly from the io module.
  * Redo python 2 bytes class.
  * Python3 compatible versions of struct.pack and struct.unpack.

  *Release 20150126*:
  bugfix py2 ustr()

  *Release 20150120*:
  cs.py3: add __contains__ to python 2 bytes type

  *Release 20150112*:
  Rerelease with separate README.rst file.

  *Release 20150111*:
  ustr: accept errors= parameter, default "strict"; update PyPI distinfo and arrangements

  *Release 20150103*:
  initial release tag for cs.py3


pkgId: df9507a4bc90bf4ccf03614fe3486281c01f55476188ce751c5ac7784f7480e8
name: python-cs.queues
arch: src
version: 20240412
epoch: 0
release: 1
summary: some Queue subclasses and ducktypes
url: https://pypi.org/project/cs.queues/
rpm_license: GNU General Public License v3 or later (GPLv3+)
rpm_group: Unspecified
rpm_buildhost: localhost
location_href: Packages/python-cs.queues-20240412-1.src.rpm
checksum_type: sha256
description:
  Queue-like items: iterable queues, channels, etc.

  *Latest release 20240412*:
  * QueueIterator: new next_batch and iter_batch methods for collecting items in batches.
  * QueueIterator: bugfix put(), do not hold the lock around the internal Queue.put, can deadlock the corresponding next/get.



  A zero-storage data passage.
  Unlike a `Queue`, `put(item)` blocks waiting for the matching `get()`.

  *Method `Channel.__init__(self)`*:
  pylint: disable=consider-using-with

  *Method `Channel.__call__(self, *a)`*:
  Call the `Channel`.
  With no arguments, do a `.get()`.
  With an argument, do a `.put()`.

  *Method `Channel.__iter__(self)`*:
  A `Channel` is iterable.

  *Method `Channel.__next__(self)`*:
  `next(Channel)` calls `Channel.get()`.

  *Method `Channel.__str__(self)`*:
  pylint: disable=consider-using-with

  *Method `Channel.close(self)`*:
  Close the `Channel`, preventing further `put()`s.

  *Method `Channel.get(self, *a, **kw)`*:
  Wrapper function to check that this instance is not closed.

  *Method `Channel.put(self, *a, **kw)`*:
  Wrapper function to check that this instance is not closed.



  Get up to `max_batch` closely spaced items from the queue `q`.
  Return the batch. Raise `Queue_Empty` if the first `q.get()` raises.

  Block until the first item arrives. While the batch's size is
  less that `max_batch` and there is another item available
  within `poll_delay` seconds, append that item to the batch.

  This requires `get_batch()` to be the sole consumer of `q`
  for correct operation as it polls `q.empty()`.



  Factory to create an iterable `PriorityQueue`.



  Factory to create an iterable queue.
  Note that the returned queue is already open
  and needs a close.



  A simple iterable queue based on a `list`.

  *Method `ListQueue.__init__(self, queued=None, *, unique=None)`*:
  Initialise the queue.

  Parameters:
  * `queued` is an optional iterable of initial items for the queue
  * `unique`: optional signature function, default `None`

  The `unique` parameter provides iteration via the
  `cs.seq.unrepeated` iterator filter which yields only items
  not seen earlier in the iteration.
  If `unique` is `None` or `False` iteration iterates
  over the queue items directly.
  If `unique` is `True`, iteration uses the default mode
  where items are compared for equality.
  Otherwise `unique` may be a callable which produces a
  value to use to detect repetitions, used as the `cs.seq.unrepeated`
  `signature` parameter.

  Example:

      >>> items = [1, 2, 3, 1, 2, 5]
      >>> list(ListQueue(items))
      [1, 2, 3, 1, 2, 5]
      >>> list(ListQueue(items, unique=True))
      [1, 2, 3, 5]

  *Method `ListQueue.__bool__(self)`*:
  A `ListQueue` looks a bit like a container,
  and is false when empty.

  *Method `ListQueue.__iter__(self)`*:
  A `ListQueue` is iterable.

  *Method `ListQueue.__next__(self)`*:
  Iteration gets from the queue.

  *Method `ListQueue.append(self, item)`*:
  Append an item to the queue, aka `put`.

  *Method `ListQueue.extend(self, items)`*:
  Convenient/performant queue-lots-of-items.

  *Method `ListQueue.get(self)`*:
  Get pops from the start of the list.

  *Method `ListQueue.insert(self, index, item)`*:
  Insert `item` at `index` in the queue.

  *Method `ListQueue.prepend(self, items, offset=0)`*:
  Insert `items` at `offset` (default `0`, the front of the queue).

  *Method `ListQueue.put(self, item)`*:
  Put appends to the queue.



  A queue-like object that discards its inputs.
  Calls to `.get()` raise `Queue_Empty`.



  A queue-like object that discards its inputs.
  Calls to `.get()` raise `Queue_Empty`.

  *Method `NullQueue.__init__(self, blocking=False, name=None)`*:
  Initialise the `NullQueue`.

  Parameters:
  * `blocking`: optional; if true, calls to `.get()` block until
    `.shutdown()`; default: `False`.
  * `name`: optional name for this `NullQueue`.

  *Method `NullQueue.get(self)`*:
  Get the next value. Always raises `Queue_Empty`.
  If `.blocking,` delay until `.shutdown()`.

  *Method `NullQueue.put(self, item)`*:
  Put a value onto the queue; it is discarded.

  *Method `NullQueue.shutdown(self)`*:
  Shut down the queue.

  *Method `NullQueue.startup(self)`*:
  Start the queue.



  A puttable object which looks like an iterable `Queue`.

  In this base class,
  calling `.put(item)` calls `functor` supplied at initialisation
  to trigger a function on data arrival
  whose iterable of results are put onto the output queue.

  As an example, the `cs.pipeline.Pipeline` class
  uses subclasses of `PushQueue` for each pipeline stage,
  overriding the `.put(item)` method
  to mediate the call of `functor` through `cs.later.Later`
  as resource controlled concurrency.

  *Method `PushQueue.__init__(self, name, functor, outQ)`*:
  Initialise the PushQueue with the callable `functor`
  and the output queue `outQ`.

  Parameters:
  * `functor` is a one-to-many function which accepts a single
    item of input and returns an iterable of outputs; it may be a
    generator. These outputs are passed to `outQ.put` individually as
    received.
  * `outQ` is a `MultiOpenMixin` which accepts via its `.put()` method.

  *Method `PushQueue.put(self, *a, **kw)`*:
  Wrapper function to check that this instance is not closed.

  *Method `PushQueue.startup_shutdown(self)`*:
  Open/close the output queue.



  A `QueueIterator` is a wrapper for a `Queue` (or ducktype) which
  presents an iterator interface to collect items.
  It does not offer the `.get` or `.get_nowait` methods.

  *Method `QueueIterator.__iter__(self)`*:
  Iterable interface for the queue.

  *Method `QueueIterator.__next__(self)`*:
  Return the next item from the queue.
  If the queue is closed, raise `StopIteration`.

  *Method `QueueIterator.empty(self)`*:
  Test if the queue is empty.

  *Method `QueueIterator.iter_batch(self, batch_size=1024)`*:
  A generator which yields batches of items from the queue.

  *Method `QueueIterator.join(self)`*:
  Wait for the queue items to complete.

  *Method `QueueIterator.next(self)`*:
  Return the next item from the queue.
  If the queue is closed, raise `StopIteration`.

  *Method `QueueIterator.next_batch(self, batch_size=1024, block_once=False)`*:
  Obtain a batch of immediately available items from the queue.
  Up to `batch_size` items will be obtained, default 1024.
  Return a list of the items.
  If the queue is empty an empty list is returned.
  If the queue is not empty, continue collecting items until
  the queue is empty or the batch size is reached.
  If `block_once` is true, wait for the first item;
  this mode never returns an empty list except at the end of the iterator.

  *Method `QueueIterator.put(self, *a, **kw)`*:
  Wrapper function to check that this instance is not closed.

  *Method `QueueIterator.startup_shutdown(self)`*:
  `MultiOpenMixin` support; puts the sentinel onto the underlying queue
  on the final close.

  *Method `QueueIterator.task_done(self)`*:
  Report that an item has been processed.



  Class to run a lot of "in the future" jobs without using a bazillion
  Timer threads.

  *Method `TimerQueue.add(self, when, func)`*:
  Queue a new job to be called at 'when'.
  'func' is the job function, typically made with `functools.partial`.

  *Method `TimerQueue.close(self, cancel=False)`*:
  Close the `TimerQueue`. This forbids further job submissions.
  If `cancel` is supplied and true, cancel all pending jobs.
  Note: it is still necessary to call `TimerQueue.join()` to
  wait for all pending jobs.

  *Method `TimerQueue.join(self)`*:
  Wait for the main loop thread to finish.





  *Release 20240412*:
  * QueueIterator: new next_batch and iter_batch methods for collecting items in batches.
  * QueueIterator: bugfix put(), do not hold the lock around the internal Queue.put, can deadlock the corresponding next/get.

  *Release 20240318*:
  QueueIterator.put: be more rigorous with the put + item count increment.

  *Release 20240316*:
  Fixed release upload artifacts.

  *Release 20240305*:
  New get_batch(q) to fetch closely spaced batches from a Queue.

  *Release 20240211*:
  ListQueue: new unique parameter which uses cs.seq.unrepeated in iteration.

  *Release 20231129*:
  * ListQueue: add __len__, __str__, __repr__.
  * QueueIterator: use a mutex around the item counting.

  *Release 20230331*:
  * QueueIterator.startup_shutdown: try/finally for the terminating _put(self.sentinel).
  * QueueIterator.__str__: show the underlying queue.
  * QueueIterator: drop call to self.finalise(), no longer available or needed.

  *Release 20221228*:
  Minor doc update.

  *Release 20221207*:
  PushQueue: modernise the MutiOpenMixin startup_shutdown, do both open and close of self.outQ.

  *Release 20220918*:
  Expose formerly private _QueueIterator as QueueIterator, use a per-queue sentinel from cs.obj.Sentinel.

  *Release 20220805*:
  ListQueue: add "append" synonym for "put" in keeping with the list-ish flavour.

  *Release 20220605*:
  ListQueue: extend/prepend: reject str explicitly - although iterable, it is almost never what is intended.

  *Release 20220317*:
  Add missed import.

  *Release 20220313*:
  New ListQueue.prepend(items[,offset=0]) method.

  *Release 20211116*:
  ListQueue: new insert() method.

  *Release 20210924*:
  Channel: make a Channel iterable.

  *Release 20210913*:
  New ListQueue simple iterable queue based on a list with list-like .append and .extend.

  *Release 20201025*:
  Drop obsolete call to MultiOpenMixin.__init__.

  *Release 20200718*:
  _QueueIterator: set finalise_later via new MultiOpenMixin property, required by recent MultiOpenMixin change.

  *Release 20200521*:
  IterableQueue,IterablePriorityQueue: simplify wrappers, bypasses weird bug from overengineering these.

  *Release 20191007*:
  * PushQueue: improve __str__.
  * Clean lint, drop cs.obj dependency.

  *Release 20190812*:
  _QueueIterator: do MultiOpenMixin.__init__ so that __str__ is functional.

  *Release 20181022*:
  Bugfix Channel, drasticly simplify PushQueue, other minor changes.

  *Release 20160828*:
  * Use "install_requires" instead of "requires" in DISTINFO.
  * TimerQueue.add: support optional *a and **kw arguments for func.
  * Many bugfixes and internal changes.

  *Release 20150115*:
  More PyPI metadata fixups.

  *Release 20150111*:
  Initial PyPI release.


