Index: pluginconf/__init__.py ================================================================== --- pluginconf/__init__.py +++ pluginconf/__init__.py @@ -16,11 +16,11 @@ # url: http://fossil.include-once.org/streamtuner2/wiki/plugin+meta+data # config: - # format: off # permissive: 0.75 # pylint: disable=invalid-name -# console-scripts: flit-pluginconf=pluginconf.flit:main, pluginconf.flit=pluginconf.flit:main +# console-scripts: flit-pluginconf=pluginconf.flit:main # # Provides plugin lookup and meta data extraction utility functions. # It's used to abstract module+option management in applications. # For consolidating internal use and external/tool accessibility. # Generally these functions are highly permissive / error tolerant, @@ -109,10 +109,11 @@ import sys import os import os.path import re import functools +import itertools import pkgutil import inspect try: from gzip import decompress as gzip_decode # Py3 only except ImportError: @@ -275,27 +276,29 @@ | frame | int | Extract comment header of caller (default). | | extra_base | list | Additional search directories. | | max_length | list | Maximum size to read from files (6K default). | | **Returns** | dict | Extracted comment fields, with config: preparsed| - The result dictionary (`PluginMeta`) has fields accessible as e.g. `pmd["title"]` - or `pmd.version`. The documentation block after all fields: is called - `["doc"]`. And `pmd.config` already parsed as a list (`OptionList`) of dictionaries. + The result dictionary (`PluginMeta`) has fields accessible as e.g. `meta["title"]` + or `meta.version`. The documentation block after all fields: is called + `meta["doc"]`. + And `meta.config` already parsed as a list (`OptionList`) of dictionaries. """ # Try via pkgutil first, # find any plugins.* modules, or main packages if module: - filename = module - for base in plugin_base + kwargs.get("extra_base", []): + search = plugin_base + kwargs.get("extra_base", []) + for base, sfx in itertools.product(search, [".py"]): try: #log.debug(f"mod={base} fn={filename}.py") - src = get_data(filename=filename+".py", decode=True, file_root=base) + src = get_data(filename=module+sfx, decode=True, file_root=base) if src: break except (IOError, OSError, FileNotFoundError): continue # plugin_meta_extract() will print a notice later + filename = module # Real filename/path elif filename and os.path.exists(filename): src = open(filename).read(kwargs.get("max_length", 6144)) @@ -303,11 +306,11 @@ elif not src and not filename: module = inspect.getmodule(sys._getframe(frame+1)) # decorator+1 filename = inspect.getsourcefile(module) src = inspect.getcomments(module) - # Assume it's a filename matches …/base.zip/…/int.py + # Assume it's a filename matching …/base.zip/…/int.py elif filename: int_fn = "" while len(filename) and not os.path.exists(filename): # pylint: disable=len-as-condition filename, add = os.path.split(filename) int_fn = add + "/" + int_fn @@ -315,12 +318,15 @@ src = zipfile.ZipFile(filename, "r").read(int_fn.strip("/")) # Extract source comment into meta dict if not src: src = "" - if isinstance(src, bytes): - src = src.decode("utf-8", errors='replace') + if hasattr(src, "decode"): + try: + src = src.decode("utf-8", errors='replace') + except UnicodeDecodeError: + pass return plugin_meta_extract(src, filename) # Comment and field extraction logic # ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ @@ -333,11 +339,11 @@ | Parameters | | | |-------------|---------|---------------------------------| | src | str | from existing source code | | filename | str | set filename attribute | - | literls | bool | just split comment from doc | + | literal | bool | just split comment from doc | | **Returns** | dict | fields | """ # Defaults meta = { @@ -557,14 +563,14 @@ a config store. Which in the case of streamtuner2 is really just a dictionary `conf{}` and a plugin list in `conf.plugins{}`. | Parameters | | | |-------------|---------|--------------------------------------| - |conf_options | dict 🔁 | storage for amassed options | - |conf_plugins | dict 🔁 | enable status based on plugin state/priority: | - |meta | dict | input plugin meta data (invoke once per plugin)| - |module | str | basename of meta: blocks plugin file | + | conf_options| dict 🔁 | storage for amassed options | + | conf_plugins| dict 🔁 | enable status based on plugin state/priority: | + | meta | dict | input plugin meta data (invoke once per plugin)| + | module | str | basename of meta: blocks plugin file | | **Returns** | None | - | """ # Option defaults, if not yet defined for opt in meta.get("config", []):