From: Dmitry Selyutin Date: Tue, 22 Aug 2017 20:54:22 +0000 (+0300) Subject: [pygnulib] properties support; file-like module class X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=bdcb0cf208fa311d6e8a9ba3b4d4b45177329b52;p=gnulib.git [pygnulib] properties support; file-like module class --- diff --git a/pygnulib/config.py b/pygnulib/config.py index faf39fdf39..b8cbf50aed 100644 --- a/pygnulib/config.py +++ b/pygnulib/config.py @@ -7,73 +7,73 @@ import os import re -class GenericConfig: +class Config: """The most basic gnulib configuration holder""" _TABLE_ = { "gnulib" : "", "root" : "", - "local-dir" : "", - "source-base" : "lib", - "m4-base" : "m4", - "po-base" : "po", - "doc-base" : "doc", - "tests-base" : "tests", - "aux-dir" : "", + "local_dir" : "", + "source_base" : "lib", + "m4_base" : "m4", + "po_base" : "po", + "doc_base" : "doc", + "tests_base" : "tests", + "aux_dir" : "", "lib" : "libgnu", - "makefile-name" : "Makefile.am", - "macro-prefix" : "gl", - "po-domain" : "", - "witness-c-macro" : "", + "makefile_name" : "Makefile.am", + "macro_prefix" : "gl", + "po_domain" : "", + "witness_c_macro" : "", "lgpl" : 0, "tests" : False, - "cxx-tests" : False, - "longrunning-tests" : False, - "privileged-tests" : False, - "unportable-tests" : False, - "all-tests" : False, + "cxx_tests" : False, + "longrunning_tests" : False, + "privileged_tests" : False, + "unportable_tests" : False, + "all_tests" : False, "libtool" : False, "conddeps" : False, - "vc-files" : False, + "vc_files" : False, "autoconf" : 2.59, - "modules" : [], - "avoids" : [], - "files" : [], } def __repr__(self): - return "pygnulib.GenericConfig" + str(self._table_) + return repr(self.__dict__["_table_"]) - def __init__(self, gnulib, root, **kwargs): - if not (os.path.isdir(gnulib) and os.path.isdir(os.path.join(gnulib, ".git"))): - raise NotADirectoryError(gnulib) - if not os.path.isdir(root): - raise NotADirectoryError(gnulib) - self._table_ = dict() - for arg in GenericConfig._TABLE_: - self._table_[arg] = GenericConfig._TABLE_[arg] - self._table_["gnulib"] = gnulib - self._table_["root"] = root - for arg in kwargs: - self[arg] = kwargs[arg] + def __init__(self, **kwargs): + self.__dict__["_table_"] = dict() + for key in Config._TABLE_: + self.__dict__["_table_"][key] = Config._TABLE_[key] + for key, value in kwargs.items(): + self[key] = value + + + def __setattr__(self, key, value): + self[key] = value + self.__dict__[key] = value + + + def __getattr__(self, key): + return self[key] def __getitem__(self, key): - if key not in GenericConfig._TABLE_: + if key not in Config._TABLE_: key = key.replace("_", "-") - if key not in GenericConfig._TABLE_: + if key not in Config._TABLE_: raise KeyError("unsupported option: '%s'" % key) - return self._table_[key] + return self.__dict__["_table_"][key] def __setitem__(self, key, value): - if key not in GenericConfig._TABLE_: + if key not in Config._TABLE_: key = key.replace("_", "-") - if key not in GenericConfig._TABLE_: + if key not in Config._TABLE_: raise KeyError("unsupported option: '%s'" % key) - typeid = type(GenericConfig._TABLE_[key]) + typeid = type(Config._TABLE_[key]) if key == "lgpl": if value not in [None, 2, 3]: raise TypeError("lgpl option must be either None or integral version (2 or 3)") @@ -86,18 +86,14 @@ class GenericConfig: tests = ["tests", "cxx-tests", "longrunning-tests", "privileged-tests", "unportable-tests"] if key == "all-tests": for _ in tests: - self._table_[_] = value + self.__dict__["_table_"][_] = value else: - self._table_[key] = value + self.__dict__["_table_"][key] = value - def path(path): - path = os.path.normpath(path) - os.path.split(os.path.sep) - -class CachedConfig(GenericConfig): - """Unlike GenericConfig, CachedConfig tries to retrieve variables from the cached files.""" +class CachedConfig(Config): + """Cached configuration holder""" _AUTOCONF_ = { "autoconf" : re.compile(".*AC_PREREQ\\(\\[(.*?)\\]\\)", re.S | re.M), "aux-dir" : re.compile("^AC_CONFIG_AUX_DIR\\(\\[(.*?)\\]\\)$", re.S | re.M), @@ -142,14 +138,15 @@ class CachedConfig(GenericConfig): _GNULIB_CACHE_PATTERN_ = re.compile("^(gl_.*?)\\(\\[(.*?)\\]\\)$", re.S | re.M) - def __init__(self, gnulib, root, autoconf=None, **kwargs): - super(CachedConfig, self).__init__(gnulib, root, **kwargs) - self._autoconf_(autoconf) - self._gnulib_cache_() - self._gnulib_comp_() + def __init__(self, root, autoconf=None, **kwargs): + if not isinstance(root, str): + raise TypeError("root must be of 'str' type") + super(CachedConfig, self).__init__(**kwargs) + self._autoconf_(root, autoconf) + self._gnulib_cache_(root) + self._gnulib_comp_(root) - def _autoconf_(self, autoconf): - root = self["root"] + def _autoconf_(self, root, autoconf): if not autoconf: autoconf = os.path.join(root, "configure.ac") if not os.path.exists(autoconf): @@ -167,8 +164,7 @@ class CachedConfig(GenericConfig): else: self[key] = match[-1] - def _gnulib_cache_(self): - root = self["root"] + def _gnulib_cache_(self, root): m4base = self["m4-base"] gnulib_cache = os.path.join(root, m4base, "gnulib-cache.m4") if os.path.exists(gnulib_cache): @@ -188,8 +184,7 @@ class CachedConfig(GenericConfig): if macro in match: self[key] = [_.strip() for _ in match[macro].split("\n") if _.strip()] - def _gnulib_comp_(self): - root = self["root"] + def _gnulib_comp_(self, root): m4base = self["m4-base"] gnulib_comp = os.path.join(root, m4base, "gnulib-comp.m4") if os.path.exists(gnulib_comp): @@ -200,7 +195,3 @@ class CachedConfig(GenericConfig): match = pattern.findall(data) if match: self["files"] = [_.strip() for _ in match[-1].split("\n") if _.strip()] - - - def __repr__(self): - return "pygnulib.CachedConfig" + str(self._table_) diff --git a/pygnulib/module.py b/pygnulib/module.py index 8fd168be27..69824f1176 100644 --- a/pygnulib/module.py +++ b/pygnulib/module.py @@ -7,79 +7,58 @@ import re import codecs + class Module: + _PATTERN_ = re.compile("") _TABLE_ = { - "description" : (0x00, str, None, "Description"), - "comment" : (0x01, str, None, "Comment"), - "status" : (0x02, str, None, "Status"), - "notice" : (0x03, str, None, "Notice"), - "applicability" : (0x04, str, None, "Applicability"), + "description" : (0x00, str, None, "Description"), + "comment" : (0x01, str, None, "Comment"), + "status" : (0x02, str, None, "Status"), + "notice" : (0x03, str, None, "Notice"), + "applicability" : (0x04, str, None, "Applicability"), "files" : (0x05, list, None, "Files"), "dependencies" : (0x06, list, None, "Depends-on"), - "preconfigure" : (0x07, str, None, "configure.ac-early"), - "configure" : (0x08, str, None, "configure.ac"), - "makefile" : (0x09, str, None, "Makefile.am"), + "preconfigure" : (0x07, str, None, "configure.ac-early"), + "configure" : (0x08, str, None, "configure.ac"), + "makefile" : (0x09, str, None, "Makefile.am"), "include" : (0x0A, list, None, "Include"), "link" : (0x0B, list, None, "Link"), - "license" : (0x0C, str, None, "License"), + "license" : (0x0C, str, None, "License"), "maintainers" : (0x0D, list, None, "Maintainer"), } - _EXCLUDE_ = { - "CVS", - "ChangeLog", - "COPYING", - "README", - "TEMPLATE", - "TEMPLATE-EXTENDED", - } - for key, (uid, typeid, _, field) in _TABLE_.items(): - suffix = ("(?:" + ":|".join([_[3] for _ in _TABLE_.values()]) + ":)") - pattern = re.compile(("^%s:\\s*(.*?)%s" % (field, suffix)), re.S | re.M) - _TABLE_[key] = (uid, typeid, pattern, field) + for _key_, (_uid_, _typeid_, _, _field_) in _TABLE_.items(): + _suffix_ = ("(?:" + ":|".join([_[3] for _ in _TABLE_.values()]) + ":)") + _pattern_ = re.compile(("^%s:\\s*(.*?)%s" % (_field_, _suffix_)), re.S | re.M) + _TABLE_[_key_] = (_uid_, _typeid_, _pattern_, _field_) - def __init__(self): - self._table_ = dict() + def __init__(self, name, **kwargs): + if not isinstance(name, str): + raise TypeError("name must be of 'str' type") + self.__dict__["_name_"] = name + self.__dict__["_table_"] = dict() + for key in Module._TABLE_: - - def __getitem__(self, key): - if key not in Module._TABLE_: - raise ValueError("unsupported key: '%s'" % key) - return self._table_[key] + self.__dict__["_table_"][key] = "" + self.__dict__["_table_"]["maintainers"] = ["all"] + for key, value in kwargs.items(): + self[key] = value - def __setitem__(self, key, value): - if key not in Module._TABLE_: - raise ValueError("unsupported key: '%s'" % key) - typeid = Module._TABLE_[key][1] - if not isinstance(value, typeid): - raise TypeError("'%s' key expects '%s' type" % (key, typeid)) - self._table_[key] = value + @property + def name(self): + return self.__dict__["_name_"] - @classmethod - def file(cls, path): - module = Module() - with codecs.open(path, "rb", "UTF-8") as stream: - data = stream.read() - for key, (_, typeid, pattern, field) in Module._TABLE_.items(): - match = pattern.findall(data) - if typeid is list: - module[key] = [_ for _ in "".join(match).split("\n") if _.strip()] - else: - module[key] = "\n".join([_.strip() for _ in match]) if match else "" - return module + def __hash__(self): + return hash(str(self)) - @classmethod - def list(cls, config): - base = os.path.join(config["gnulib"], "modules") - for root, _, files in os.walk(base): - for file in [_ for _ in files if _ not in Module._EXCLUDE_]: - yield file + def __repr__(self): + return self.__dict__["_name_"] - def package(self): + def __str__(self): result = "" for key, (_, typeid, _, field) in sorted(Module._TABLE_.items(), key=lambda k: k[1][0]): field += ":\n" @@ -87,10 +66,106 @@ class Module: value = "\n".join(self[key]) else: value = self[key] - if key == "maintainers" and not value: - value = "all" if value: result += field result += value result += "\n\n" if value else "\n" return result.strip() + "\n" + + + def __setattr__(self, key, value): + self[key] = value + self.__dict__[key] = value + + + def __getattr__(self, key): + return self[key] + + + def __getitem__(self, key): + if key not in Module._TABLE_: + raise ValueError("unsupported key: '%s'" % key) + return self.__dict__["_table_"][key] + + + def __setitem__(self, key, value): + if key not in Module._TABLE_: + raise ValueError("unsupported key: '%s'" % key) + typeid = Module._TABLE_[key][1] + if not isinstance(value, typeid): + raise TypeError("'%s' key expects '%s' type" % (key, typeid)) + self.__dict__["_table_"][key] = value + + + def __lt__(self, value): + if isinstance(value, Module): + return (self != value) and (self.__dict__["_name_"] < value.name) + return TypeError("cannot compare pygnulib.Module with %r type" % type(value)) + + def __le__(self, value): + return self.__lt__(value) or self.__eq__(value) + + def __eq__(self, value): + if isinstance(value, Module): + return (self.__dict__["_name_"] == value.__dict__["_name_"]) \ + and (self.__dict__["_table_"] == value.__dict__["_name_"]) + return TypeError("cannot compare pygnulib.Module with %r type" % type(value)) + + def __ne__(self, value): + return not self.__eq__(value) + + def __ge__(self, value): + if isinstance(value, Module): + return value.__le__(self) + return TypeError("cannot compare pygnulib.Module with %r type" % type(value)) + + def __gt__(self, value): + if isinstance(value, Module): + return value.__lt__(self) + return TypeError("cannot compare pygnulib.Module with %r type" % type(value)) + + + +class FileModule(Module): + """Read or modify existing gnulib module package""" + + def __init__(self, path, mode="r", name=None, **kwargs): + if name is None: + name = os.path.basename(path) + if mode not in ("r", "w", "rw"): + raise ValueError("illegal mode: %r" % mode) + if mode == "r": + super(FileModule, self).__init__(name) + with codecs.open(path, "rb", "UTF-8") as stream: + data = stream.read() + for key, (_, typeid, pattern, _) in Module._TABLE_.items(): + match = pattern.findall(data) + self[key] = [_ for _ in "".join(match).split("\n") if _.strip()] \ + if typeid is list else \ + ("\n".join([_.strip() for _ in match]) if match else "") + self.__dict__["_stream_"] = None + elif mode == "w": + super(FileModule, self).__init__(name) + self.__dict__["_stream_"] = codecs.open(path, "w+", "UTF-8") + elif mode == "rw": + self.__init__(path, "r") + self.__dict__["_stream_"] = codecs.open(path, "w+", "UTF-8") + else: + raise ValueError("invalid mode: %r" % mode) + for key, value in kwargs.items(): + self[key] = value + + + def close(self): + if self.__dict__["_stream_"]: + self.__dict__["_stream_"].truncate(0) + self.__dict__["_stream_"].write(str(self)) + self.__dict__["_stream_"].close() + + + def __enter__(self): + return self + + + def __exit__(self, exctype, excval, exctrace): + self.close()