summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Pokorný <jpokorny@redhat.com>2014-08-29 21:59:01 +0200
committerJan Pokorný <jpokorny@redhat.com>2014-08-29 23:39:12 +0200
commit8383fb1342bf639234f5fc1ee6534f055a558bee (patch)
tree47667fdd68db3d511969d579cc1803a6159cc7ff
parent7534c5f0c5a7da2d4f684129639de41fb0772a18 (diff)
downloadclufter-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.py42
-rw-r--r--formats/ccs.py2
-rw-r--r--formats/coro.py4
-rw-r--r--formats/pcs.py2
-rw-r--r--formats/simpleconfig.py12
-rw-r--r--plugin_registry.py10
-rw-r--r--protocol.py41
7 files changed, 81 insertions, 32 deletions
diff --git a/format.py b/format.py
index d33a866..edc17c3 100644
--- a/format.py
+++ b/format.py
@@ -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)