#!/usr/bin/env python
from setuptools import setup
setup(
  name = 'cs.deco',
  author = 'Cameron Simpson',
  author_email = 'cs@cskk.id.au',
  version = '20200517',
  url = 'https://bitbucket.org/cameron_simpson/css/commits/all',
  description =
    'Assorted decorator functions.',
  long_description =
    ('Assorted decorator functions.\n'    
 '\n'    
 '*Latest release 20200517*:\n'    
 'Get warning() from cs.gimmicks.\n'    
 '\n'    
 '## Function `cached(*args, **kwargs)`\n'    
 '\n'    
 '@OBSOLETE(metadeco)\n'    
 '\n'    
 'Decorator to cache the result of an instance or class method\n'    
 '     and keep a revision counter for changes.\n'    
 '\n'    
 '     The cached values are stored on the instance (`self`).\n'    
 '     The revision counter supports the `@revised` decorator.\n'    
 '\n'    
 '     This decorator may be used in 2 modes.\n'    
 '     Directly:\n'    
 '\n'    
 '         @cachedmethod\n'    
 '         def method(self, ...)\n'    
 '\n'    
 '     or indirectly:\n'    
 '\n'    
 '         @cachedmethod(poll_delay=0.25)\n'    
 '         def method(self, ...)\n'    
 '\n'    
 '     Optional keyword arguments:\n'    
 '     * `attr_name`: the basis name for the supporting attributes.\n'    
 '       Default: the name of the method.\n'    
 '     * `poll_delay`: minimum time between polls; after the first\n'    
 '       access, subsequent accesses before the `poll_delay` has elapsed\n'    
 '       will return the cached value.\n'    
 '       Default: `None`, meaning no poll delay.\n'    
 '     * `sig_func`: a signature function, which should be significantly\n'    
 '       cheaper than the method. If the signature is unchanged, the\n'    
 '       cached value will be returned. The signature function\n'    
 '       expects the instance (`self`) as its first parameter.\n'    
 '       Default: `None`, meaning no signature function;\n'    
 '       the first computed value will be kept and never updated.\n'    
 '     * `unset_value`: the value to return before the method has been\n'    
 '       called successfully.\n'    
 '       Default: `None`.\n'    
 '\n'    
 '     If the method raises an exception, this will be logged and\n'    
 '     the method will return the previously cached value,\n'    
 '     unless there is not yet a cached value\n'    
 '     in which case the exception will be reraised.\n'    
 '\n'    
 '     If the signature function raises an exception\n'    
 '     then a log message is issued and the signature is considered '    
 'unchanged.\n'    
 '\n'    
 '     An example use of this decorator might be to keep a "live"\n'    
 '     configuration data structure, parsed from a configuration\n'    
 '     file which might be modified after the program starts. One\n'    
 '     might provide a signature function which called `os.stat()` on\n'    
 '     the file to check for changes before invoking a full read and\n'    
 '     parse of the file.\n'    
 '\n'    
 '     *Note*: use of this decorator requires the `cs.pfx` module.\n'    
 '\n'    
 '## Function `cachedmethod(*da, **dkw)`\n'    
 '\n'    
 'Decorator to cache the result of an instance or class method\n'    
 'and keep a revision counter for changes.\n'    
 '\n'    
 'The cached values are stored on the instance (`self`).\n'    
 'The revision counter supports the `@revised` decorator.\n'    
 '\n'    
 'This decorator may be used in 2 modes.\n'    
 'Directly:\n'    
 '\n'    
 '    @cachedmethod\n'    
 '    def method(self, ...)\n'    
 '\n'    
 'or indirectly:\n'    
 '\n'    
 '    @cachedmethod(poll_delay=0.25)\n'    
 '    def method(self, ...)\n'    
 '\n'    
 'Optional keyword arguments:\n'    
 '* `attr_name`: the basis name for the supporting attributes.\n'    
 '  Default: the name of the method.\n'    
 '* `poll_delay`: minimum time between polls; after the first\n'    
 '  access, subsequent accesses before the `poll_delay` has elapsed\n'    
 '  will return the cached value.\n'    
 '  Default: `None`, meaning no poll delay.\n'    
 '* `sig_func`: a signature function, which should be significantly\n'    
 '  cheaper than the method. If the signature is unchanged, the\n'    
 '  cached value will be returned. The signature function\n'    
 '  expects the instance (`self`) as its first parameter.\n'    
 '  Default: `None`, meaning no signature function;\n'    
 '  the first computed value will be kept and never updated.\n'    
 '* `unset_value`: the value to return before the method has been\n'    
 '  called successfully.\n'    
 '  Default: `None`.\n'    
 '\n'    
 'If the method raises an exception, this will be logged and\n'    
 'the method will return the previously cached value,\n'    
 'unless there is not yet a cached value\n'    
 'in which case the exception will be reraised.\n'    
 '\n'    
 'If the signature function raises an exception\n'    
 'then a log message is issued and the signature is considered unchanged.\n'    
 '\n'    
 'An example use of this decorator might be to keep a "live"\n'    
 'configuration data structure, parsed from a configuration\n'    
 'file which might be modified after the program starts. One\n'    
 'might provide a signature function which called `os.stat()` on\n'    
 'the file to check for changes before invoking a full read and\n'    
 'parse of the file.\n'    
 '\n'    
 '*Note*: use of this decorator requires the `cs.pfx` module.\n'    
 '\n'    
 '## Function `contextual(func)`\n'    
 '\n'    
 'Wrap a simple function as a context manager.\n'    
 '\n'    
 'This was written to support users of `@strable`,\n'    
 'which requires its `open_func` to be a context manager;\n'    
 'this turns an arbitrary function into a context manager.\n'    
 '\n'    
 'Example promoting a trivial function:\n'    
 '\n'    
 '    >>> f = lambda: 3\n'    
 '    >>> cf = contextual(f)\n'    
 '    >>> with cf() as x: print(x)\n'    
 '    3\n'    
 '\n'    
 '## Function `decorator(deco)`\n'    
 '\n'    
 'Wrapper for decorator functions to support optional arguments.\n'    
 '\n'    
 'The actual decorator function ends up being called as:\n'    
 '\n'    
 '    mydeco(func, *da, **dkw)\n'    
 '\n'    
 'allowing `da` and `dkw` to affect the behaviour of the decorator `mydeco`.\n'    
 '\n'    
 'Examples:\n'    
 '\n'    
 '    @decorator\n'    
 '    def mydeco(func, *da, kw=None):\n'    
 '      ... decorate func subject to the values of da and kw\n'    
 '\n'    
 '    @mydeco\n'    
 '    def func1(...):\n'    
 '      ...\n'    
 '\n'    
 "    @mydeco('foo', arg2='bah')\n"    
 '    def func2(...):\n'    
 '      ...\n'    
 '\n'    
 '## Function `fmtdoc(func)`\n'    
 '\n'    
 "Decorator to replace a function's docstring with that string\n"    
 "formatted against the function's module `__dict__`.\n"    
 '\n'    
 'This supports simple formatted docstrings:\n'    
 '\n'    
 "    ENVVAR_NAME = 'FUNC_DEFAULT'\n"    
 '\n'    
 '    @fmtdoc\n'    
 '    def func():\n'    
 '        """Do something with os.environ[{ENVVAR_NAME}]."""\n'    
 '        print(os.environ[ENVVAR_NAME])\n'    
 '\n'    
 'This gives `func` this docstring:\n'    
 '\n'    
 '    Do something with os.environ[FUNC_DEFAULT].\n'    
 '\n'    
 '*Warning*: this decorator is intended for wiring "constants"\n'    
 'into docstrings, not for dynamic values. Use for other types\n'    
 'of values should be considered with trepidation.\n'    
 '\n'    
 '## Function `logging_wrapper(*da, **dkw)`\n'    
 '\n'    
 'Decorator for logging call shims\n'    
 'which bumps the `stacklevel` keyword argument so that the logging system\n'    
 'chooses the correct frame to cite in messages.\n'    
 '\n'    
 'Note: has no effect on Python < 3.8 because `stacklevel` only\n'    
 'appeared in that version.\n'    
 '\n'    
 '## Function `observable_class(property_names, only_unequal=False)`\n'    
 '\n'    
 'Class decorator to make various instance attributes observable.\n'    
 '\n'    
 'Parameters:\n'    
 '* `property_names`:\n'    
 '  an interable of instance property names to set up as\n'    
 '  observable properties. As a special case a single `str` can\n'    
 '  be supplied if only one attribute is to be observed.\n'    
 '* `only_unequal`:\n'    
 '  only call the observers if the new property value is not\n'    
 '  equal to the previous proerty value. This requires property\n'    
 '  values to be comparable for inequality.\n'    
 '  Default: `False`, meaning that all updates will be reported.\n'    
 '\n'    
 '## Function `OBSOLETE(*da, **dkw)`\n'    
 '\n'    
 'Decorator for obsolete functions.\n'    
 '\n'    
 'Use:\n'    
 '\n'    
 '    @OBSOLETE\n'    
 '    def func(...):\n'    
 '\n'    
 'This emits a warning log message before calling the decorated function.\n'    
 '\n'    
 '## Function `strable(*da, **dkw)`\n'    
 '\n'    
 'Decorator for functions which may accept a `str`\n'    
 'instead of their core type.\n'    
 '\n'    
 'Parameters:\n'    
 '* `func`: the function to decorate\n'    
 '* `open_func`: the "open" factory to produce the core type\n'    
 '  if a string is provided;\n'    
 '  the default is the builtin "open" function.\n'    
 '  The returned value should be a context manager.\n'    
 '  Simpler functions can be decorated with `@contextual`\n'    
 '  to turn them into context managers if need be.\n'    
 '\n'    
 'The usual (and default) example is a function to process an\n'    
 'open file, designed to be handed a file object but which may\n'    
 'be called with a filename. If the first argument is a `str`\n'    
 'then that file is opened and the function called with the\n'    
 'open file.\n'    
 '\n'    
 'Examples:\n'    
 '\n'    
 '    @strable\n'    
 '    def count_lines(f):\n'    
 '      return len(line for line in f)\n'    
 '\n'    
 '    class Recording:\n'    
 '      "Class representing a video recording."\n'    
 '      ...\n'    
 '    @strable(open_func=Recording)\n'    
 '    def process_video(r):\n'    
 '      ... do stuff with `r` as a Recording instance ...\n'    
 '\n'    
 '*Note*: use of this decorator requires the `cs.pfx` module.\n'    
 '\n'    
 '# Release Log\n'    
 '\n'    
 '\n'    
 '\n'    
 '*Release 20200517*:\n'    
 'Get warning() from cs.gimmicks.\n'    
 '\n'    
 '*Release 20200417*:\n'    
 '* @decorator: do not override __doc__ on the decorated function, just '    
 'provide default.\n'    
 '* New @logging_wrapper which bumps the `stacklevel` parameter in Python 3.8 '    
 'and above so that shims recite the correct caller.\n'    
 '\n'    
 '*Release 20200318.1*:\n'    
 'New @OBSOLETE to issue a warning on a call to an obsolete function, like an '    
 'improved @cs.logutils.OBSOLETE (which needs to retire).\n'    
 '\n'    
 '*Release 20200318*:\n'    
 '@cachedmethod: tighten up the "is the value changed" try/except.\n'    
 '\n'    
 '*Release 20191012*:\n'    
 '* New @contextual decorator to turn a simple function into a context '    
 'manager.\n'    
 '* @strable: mention context manager requirement and @contextual as '    
 'workaround.\n'    
 '\n'    
 '*Release 20191006*:\n'    
 'Rename @cached to @cachedmethod, leave compatible @cached behind which '    
 'issues a warning (will be removed in a future release).\n'    
 '\n'    
 '*Release 20191004*:\n'    
 'Avoid circular import with cs.pfx by removing requirement and doing the '    
 'import later if needed.\n'    
 '\n'    
 '*Release 20190905*:\n'    
 'Bugfix @deco: it turns out that you may not set the .__module__ attribute on '    
 'a property object.\n'    
 '\n'    
 '*Release 20190830.2*:\n'    
 'Make some getattr calls robust.\n'    
 '\n'    
 '*Release 20190830.1*:\n'    
 '@decorator: set the __module__ of the wrapper.\n'    
 '\n'    
 '*Release 20190830*:\n'    
 '@decorator: set the __module__ of the wrapper from the decorated target, '    
 'aids cs.distinf.\n'    
 '\n'    
 '*Release 20190729*:\n'    
 '@cached: sidestep uninitialised value.\n'    
 '\n'    
 '*Release 20190601.1*:\n'    
 '@strable: fix the example in the docstring.\n'    
 '\n'    
 '*Release 20190601*:\n'    
 '* Bugfix @decorator to correctly propagate the docstring of the '    
 'subdecorator.\n'    
 '* Improve other docstrings.\n'    
 '\n'    
 '*Release 20190526*:\n'    
 '@decorator: add support for positional arguments and rewrite - simpler and '    
 'clearer.\n'    
 '\n'    
 '*Release 20190512*:\n'    
 '@fmtdoc: add caveat against misuse of this decorator.\n'    
 '\n'    
 '*Release 20190404*:\n'    
 "New @fmtdoc decorator to format a function's doctsring against its module's "    
 'globals.\n'    
 '\n'    
 '*Release 20190403*:\n'    
 '* @cached: bugfix: avoid using unset sig_func value on first pass.\n'    
 '* @observable_class: further tweaks.\n'    
 '\n'    
 '*Release 20190322.1*:\n'    
 '@observable_class: bugfix __init__ wrapper function.\n'    
 '\n'    
 '*Release 20190322*:\n'    
 '* New class decorator @observable_class.\n'    
 '* Bugfix import of "warning".\n'    
 '\n'    
 '*Release 20190309*:\n'    
 '@cached: improve the exception handling.\n'    
 '\n'    
 '*Release 20190307.2*:\n'    
 'Fix docstring typo.\n'    
 '\n'    
 '*Release 20190307.1*:\n'    
 'Bugfix @decorator: final plumbing step for decorated decorator.\n'    
 '\n'    
 '*Release 20190307*:\n'    
 '* @decorator: drop unused arguments, they get used by the returned '    
 'decorator.\n'    
 '* Rework the @cached logic.\n'    
 '\n'    
 '*Release 20190220*:\n'    
 '* Bugfix @decorator decorator, do not decorate twice.\n'    
 "* Have a cut at inheriting the decorated function's docstring.\n"    
 '\n'    
 '*Release 20181227*:\n'    
 '* New decoartor @strable for function which may accept a str instead of '    
 'their primary type.\n'    
 '* Improvements to @cached.\n'    
 '\n'    
 '*Release 20171231*:\n'    
 'Initial PyPI release.'),
  classifiers = ['Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)'],
  install_requires = ['cs.gimmicks'],
  keywords = ['python2', 'python3'],
  license = 'GNU General Public License v3 or later (GPLv3+)',
  long_description_content_type = 'text/markdown',
  package_dir = {'': 'lib/python'},
  py_modules = ['cs.deco'],
)
