diff options
author | Jan Pokorný <jpokorny@redhat.com> | 2014-08-29 21:59:01 +0200 |
---|---|---|
committer | Jan Pokorný <jpokorny@redhat.com> | 2014-08-29 23:39:12 +0200 |
commit | 8383fb1342bf639234f5fc1ee6534f055a558bee (patch) | |
tree | 47667fdd68db3d511969d579cc1803a6159cc7ff | |
parent | 7534c5f0c5a7da2d4f684129639de41fb0772a18 (diff) | |
download | clufter-8383fb1342bf639234f5fc1ee6534f055a558bee.tar.gz clufter-8383fb1342bf639234f5fc1ee6534f055a558bee.tar.xz clufter-8383fb1342bf639234f5fc1ee6534f055a558bee.zip |
(Optionally) promote protocols to first-class entity
+ reflect that on the formats' level (also bring a convention for that)
+ make plugin_registry.probe fnc more tolerant towards non-classes
as we want to differentiate between protocol on instance basis
rather than based on dedicated classes (because we want to
pragmatically maintain interchangeability with plain strings!)
+ make extra steps in protocol.py so the mentioned interchangeability
actually works
Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
-rw-r--r-- | format.py | 42 | ||||
-rw-r--r-- | formats/ccs.py | 2 | ||||
-rw-r--r-- | formats/coro.py | 4 | ||||
-rw-r--r-- | formats/pcs.py | 2 | ||||
-rw-r--r-- | formats/simpleconfig.py | 12 | ||||
-rw-r--r-- | plugin_registry.py | 10 | ||||
-rw-r--r-- | protocol.py | 41 |
7 files changed, 81 insertions, 32 deletions
@@ -20,6 +20,7 @@ from lxml import etree from .error import ClufterError from .plugin_registry import MetaPlugin, PluginRegistry +from .protocol import Protocol from .utils import arg2wrapped, args2tuple, args2unwrapped, \ classproperty, \ head_tail, \ @@ -291,7 +292,8 @@ class Format(object): class SimpleFormat(Format, MetaPlugin): """This is what most of the format classes want to subclass""" - native_protocol = 'bytestring' + native_protocol = BYTESTRING = Protocol('bytestring') + FILE = Protocol('file') def __init__(self, protocol, *args, **kwargs): """Format constructor, i.e., object = concrete uniformat data""" @@ -300,37 +302,37 @@ class SimpleFormat(Format, MetaPlugin): .format(self.__class__.name, protocol) super(SimpleFormat, self).__init__(protocol, *args, **kwargs) - @Format.producing('bytestring') + @Format.producing(BYTESTRING) def get_bytestring(self, protocol): - if 'file' in self._representations: # break the possible loop - with file(self('file'), 'rb') as f: + if self.FILE in self._representations: # break the possible loop + with file(self(self.FILE), 'rb') as f: return f.read() - @Format.producing('file') + @Format.producing(FILE) def get_file(self, protocol, outfile): if hasattr(outfile, 'write'): # assume fileobj out of our control, do not close - outfile.write(self('bytestring')) + outfile.write(self(self.BYTESTRING)) return outfile.name assert isinstance(outfile, basestring) if outfile == '-' or outfile.rstrip('0123456789') == '@': if outfile == '-': - stdout.write(self('bytestring')) + stdout.write(self(self.BYTESTRING)) else: warn("@DIGIT+ in get_file deprecated, implicit handling fail?", DeprecationWarning) with fdopen(int(outfile[1:]), 'ab') as f: - f.write(self('bytestring')) + f.write(self(self.BYTESTRING)) else: with file(outfile, 'wb') as f: - f.write(self('bytestring')) + f.write(self(self.BYTESTRING)) return outfile - @staticmethod - def io_decl_fd(io_decl): + @classmethod + def io_decl_fd(cls, io_decl): """Return file descriptor (int) if conforms to "magic file" or None""" - if tuplist(io_decl) and len(io_decl) >= 2 and io_decl[0] == 'file': + if tuplist(io_decl) and len(io_decl) >= 2 and io_decl[0] == cls.FILE: if io_decl[1].rstrip('0123456789') == '@': return int(io_decl[1][1:]) return None @@ -538,14 +540,16 @@ class XML(SimpleFormat): ### - native_protocol = 'etree' + native_protocol = ETREE = Protocol('etree') + BYTESTRING = SimpleFormat.BYTESTRING + validator_specs = { - 'etree': '*' # grab whatever you'll find (with backtrack) + ETREE: '*' # grab whatever you'll find (with backtrack) } @classmethod def etree_rng_validator(cls, et, root_dir=DEFAULT_ROOT_DIR, - spec=validator_specs['etree'], start=None): + spec=validator_specs[ETREE], start=None): """RNG-validate `et` ElementTree with schemes as per `root_dir`+`spec` ... and, optionally, narrowed to `start`-defined grammar segment. @@ -614,13 +618,13 @@ class XML(SimpleFormat): etree_validator = etree_rng_validator - @SimpleFormat.producing('bytestring', chained=True) + @SimpleFormat.producing(BYTESTRING, chained=True) def get_bytestring(self, protocol): # chained fallback - return etree.tostring(self('etree', protect_safe=True), + return etree.tostring(self(self.ETREE, protect_safe=True), pretty_print=True) - @SimpleFormat.producing('etree', protect=True, + @SimpleFormat.producing(ETREE, protect=True, validator=etree_validator.__func__) def get_etree(self, protocol): - return etree.fromstring(self('bytestring')).getroottree() + return etree.fromstring(self(self.BYTESTRING)).getroottree() diff --git a/formats/ccs.py b/formats/ccs.py index 244d308..6f0dff1 100644 --- a/formats/ccs.py +++ b/formats/ccs.py @@ -16,7 +16,7 @@ class ccs(XML): # XML root = 'cluster' validator_specs = { - 'etree': '' # XXX no RNG schema handy yet + XML.ETREE: '' # XXX no RNG schema handy yet } diff --git a/formats/coro.py b/formats/coro.py index f448906..492da00 100644 --- a/formats/coro.py +++ b/formats/coro.py @@ -16,12 +16,12 @@ class coroxml(XML): # XMLFormat root = 'corosync' validator_specs = { - 'etree': '' # XXX no RNG schema handy yet + XML.ETREE: '' # XXX no RNG schema handy yet } class coroxml_needle(coroxml): """Corosync (v2. needle) executive configuration, XML version""" validator_specs = { - 'etree': 'corosync.rng' + XML.ETREE: 'corosync.rng' } diff --git a/formats/pcs.py b/formats/pcs.py index 527d30b..3c5d36b 100644 --- a/formats/pcs.py +++ b/formats/pcs.py @@ -16,5 +16,5 @@ class pcs(XML): # XML root = 'cib' validator_specs = { - 'etree': 'pacemaker-1.2.rng' + XML.ETREE: 'pacemaker-1.2.rng' } diff --git a/formats/simpleconfig.py b/formats/simpleconfig.py index d5ff5a4..5d34d7f 100644 --- a/formats/simpleconfig.py +++ b/formats/simpleconfig.py @@ -6,6 +6,7 @@ __author__ = "Jan Pokorný <jpokorny @at@ Red Hat .dot. com>" from ..format import SimpleFormat +from ..protocol import Protocol from ..utils import tuplist from ..utils_func import apply_aggregation_preserving_depth @@ -40,9 +41,10 @@ class simpleconfig(SimpleFormat): None]]) """ # NOTE yacc-based parser in fence-virt - native_protocol = 'struct' + native_protocol = STRUCT = Protocol('struct') + BYTESTRING = SimpleFormat.BYTESTRING - @SimpleFormat.producing('bytestring') + @SimpleFormat.producing(BYTESTRING) def get_bytestring(self, protocol): """Externalize 'struct', that is basically, pretty print it @@ -69,12 +71,12 @@ class simpleconfig(SimpleFormat): } """ # try to look (indirectly) if we have a file at hand first - ret = super(simpleconfig, self).get_bytestring('bytestring') + ret = super(simpleconfig, self).get_bytestring(self.BYTESTRING) if ret is not None: return ret # fallback - struct = self('struct', protect_safe=True) + struct = self(self.STRUCT, protect_safe=True) indent, optindent = ('\t', ) * 2 lbrace, rbrace, optsep = '{', '}', ': ' # XXX previous apply_aggregation_preserving_passing_depth attempt @@ -112,7 +114,7 @@ class simpleconfig(SimpleFormat): ret = '\n'.join(ret.splitlines()[1:-1]) + '\n' return ret - @SimpleFormat.producing('struct', protect=True) + @SimpleFormat.producing(STRUCT, protect=True) def get_struct(self, protocol): # TODO parsing struct from string raise NotImplementedError diff --git a/plugin_registry.py b/plugin_registry.py index f27f5c7..ec21a9d 100644 --- a/plugin_registry.py +++ b/plugin_registry.py @@ -12,8 +12,8 @@ from os.path import abspath, dirname, join, splitext from contextlib import contextmanager from sys import modules -from .utils import classproperty, hybridproperty -from .utils_prog import cli_decor +from .utils import classproperty, hybridproperty, tuplist +from .utils_prog import cli_decor, cli_undecor log = logging.getLogger(__name__) @@ -70,10 +70,11 @@ class PluginRegistry(type): # @classmethod - def probe(registry, name, bases, attrs): + def probe(registry, name, bases, attrs=None): """Meta-magic to register plugin""" assert '-' not in name, "name cannot contain a dash" name = cli_decor(name) + attrs = attrs or {} try: ret = registry._plugins[name] log.info("Probe `{0}' plugin under `{1}' registry: already tracked" @@ -81,7 +82,8 @@ class PluginRegistry(type): except KeyError: log.debug("Probe `{0}' plugin under `{1}' registry: yet untracked" .format(name, registry.registry)) - ret = super(PluginRegistry, registry).__new__(registry, name, + ret = bases if not tuplist(bases) else \ + super(PluginRegistry, registry).__new__(registry, name, bases, attrs) registry._plugins[name] = ret finally: diff --git a/protocol.py b/protocol.py new file mode 100644 index 0000000..54c81d4 --- /dev/null +++ b/protocol.py @@ -0,0 +1,41 @@ +# -*- coding: UTF-8 -*- +# Copyright 2014 Red Hat, Inc. +# Part of clufter project +# Licensed under GPLv2+ (a copy included | http://gnu.org/licenses/gpl-2.0.txt) +"""Base protocol stuff (metaclass, etc.)""" +__author__ = "Jan Pokorný <jpokorny @at@ Red Hat .dot. com>" + +import logging + +from .plugin_registry import PluginRegistry +from .utils import tuplist +from .utils_prog import cli_undecor + +log = logging.getLogger(__name__) + + +class protocols(PluginRegistry): + """Protocol registry (to be used as a metaclass for filters) + + To be noted, this harness is solely optional, and only good to allow + early discovery of the typos in the protocols and to maintain some + in-code documentation of their usage as opposed to much further + in the processing with some files already successfully produced, etc. + """ + @classmethod + def register(registry, pr): + # undecor to pass the checks in probe + return registry.probe(cli_undecor(pr), + pr if isinstance(pr, Protocol) else Protocol(pr)) + + +class Protocol(str): + """Class intended to be (exceptionally) instantioned (enhanced string)""" + __metaclass__ = protocols + + def __new__(cls, *args, **kwargs): + ret = super(Protocol, cls).__new__(cls, *args, **kwargs) + return protocols.register(ret) + + def ensure_proto(self, value): + return value if tuplist(value) else (self, value) |