فهرست منبع

add versioneer

Christian Mönch 2 سال پیش
والد
کامیت
699b503b88
9فایلهای تغییر یافته به همراه620 افزوده شده و 558 حذف شده
  1. 1 0
      .gitattributes
  2. 2 0
      MANIFEST.in
  3. 4 0
      dataladmetadatamodel/__init__.py
  4. 162 59
      dataladmetadatamodel/_version.py
  5. 0 1
      dataladmetadatamodel/version.py
  6. 6 6
      setup.cfg
  7. 7 5
      setup.py
  8. 0 291
      simple_test/persisted_object.py
  9. 438 196
      versioneer.py

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+dataladmetadatamodel/_version.py export-subst

+ 2 - 0
MANIFEST.in

@@ -0,0 +1,2 @@
+include versioneer.py
+include dataladmetadatamodel/_version.py

+ 4 - 0
dataladmetadatamodel/__init__.py

@@ -25,3 +25,7 @@ def check_serialized_version(json_object: JSONObject):
             f"Unsupported metadata version ({stored_version}) in "
             f"stored {stored_class} object, expected version: "
             f"{version_string}")
+
+
+from . import _version
+__version__ = _version.get_versions()['version']

+ 162 - 59
dataladmetadatamodel/_version.py

@@ -6,7 +6,7 @@
 # that just contains the computed version number.
 
 # This file is released into the public domain. Generated by
-# versioneer-0.18 (https://github.com/warner/python-versioneer)
+# versioneer-0.20 (https://github.com/python-versioneer/python-versioneer)
 
 """Git implementation of _version.py."""
 
@@ -30,7 +30,7 @@ def get_keywords():
     return keywords
 
 
-class VersioneerConfig:
+class VersioneerConfig:  # pylint: disable=too-few-public-methods
     """Container for Versioneer configuration parameters."""
 
 
@@ -43,7 +43,7 @@ def get_config():
     cfg.style = "pep440"
     cfg.tag_prefix = ""
     cfg.parentdir_prefix = ""
-    cfg.versionfile_source = "datalad_metalad/_version.py"
+    cfg.versionfile_source = "dataladmetadatamodel/_version.py"
     cfg.verbose = False
     return cfg
 
@@ -57,7 +57,7 @@ HANDLERS = {}
 
 
 def register_vcs_handler(vcs, method):  # decorator
-    """Decorator to mark a method as the handler for a particular VCS."""
+    """Create decorator to mark a method as the handler of a VCS."""
     def decorate(f):
         """Store f in HANDLERS[vcs][method]."""
         if vcs not in HANDLERS:
@@ -67,19 +67,20 @@ def register_vcs_handler(vcs, method):  # decorator
     return decorate
 
 
+# pylint:disable=too-many-arguments,consider-using-with # noqa
 def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
                 env=None):
     """Call the given command(s)."""
     assert isinstance(commands, list)
-    p = None
-    for c in commands:
+    process = None
+    for command in commands:
         try:
-            dispcmd = str([c] + args)
+            dispcmd = str([command] + args)
             # remember shell=False, so use git.cmd on windows, not just git
-            p = subprocess.Popen([c] + args, cwd=cwd, env=env,
-                                 stdout=subprocess.PIPE,
-                                 stderr=(subprocess.PIPE if hide_stderr
-                                         else None))
+            process = subprocess.Popen([command] + args, cwd=cwd, env=env,
+                                       stdout=subprocess.PIPE,
+                                       stderr=(subprocess.PIPE if hide_stderr
+                                               else None))
             break
         except EnvironmentError:
             e = sys.exc_info()[1]
@@ -93,15 +94,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
         if verbose:
             print("unable to find command, tried %s" % (commands,))
         return None, None
-    stdout = p.communicate()[0].strip()
-    if sys.version_info[0] >= 3:
-        stdout = stdout.decode()
-    if p.returncode != 0:
+    stdout = process.communicate()[0].strip().decode()
+    if process.returncode != 0:
         if verbose:
             print("unable to run %s (error)" % dispcmd)
             print("stdout was %s" % stdout)
-        return None, p.returncode
-    return stdout, p.returncode
+        return None, process.returncode
+    return stdout, process.returncode
 
 
 def versions_from_parentdir(parentdir_prefix, root, verbose):
@@ -113,15 +112,14 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):
     """
     rootdirs = []
 
-    for i in range(3):
+    for _ in range(3):
         dirname = os.path.basename(root)
         if dirname.startswith(parentdir_prefix):
             return {"version": dirname[len(parentdir_prefix):],
                     "full-revisionid": None,
                     "dirty": False, "error": None, "date": None}
-        else:
-            rootdirs.append(root)
-            root = os.path.dirname(root)  # up a level
+        rootdirs.append(root)
+        root = os.path.dirname(root)  # up a level
 
     if verbose:
         print("Tried directories %s but none started with prefix %s" %
@@ -138,21 +136,20 @@ def git_get_keywords(versionfile_abs):
     # _version.py.
     keywords = {}
     try:
-        f = open(versionfile_abs, "r")
-        for line in f.readlines():
-            if line.strip().startswith("git_refnames ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["refnames"] = mo.group(1)
-            if line.strip().startswith("git_full ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["full"] = mo.group(1)
-            if line.strip().startswith("git_date ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["date"] = mo.group(1)
-        f.close()
+        with open(versionfile_abs, "r") as fobj:
+            for line in fobj:
+                if line.strip().startswith("git_refnames ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["refnames"] = mo.group(1)
+                if line.strip().startswith("git_full ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["full"] = mo.group(1)
+                if line.strip().startswith("git_date ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["date"] = mo.group(1)
     except EnvironmentError:
         pass
     return keywords
@@ -161,10 +158,14 @@ def git_get_keywords(versionfile_abs):
 @register_vcs_handler("git", "keywords")
 def git_versions_from_keywords(keywords, tag_prefix, verbose):
     """Get version information from git keywords."""
-    if not keywords:
-        raise NotThisMethod("no keywords at all, weird")
+    if "refnames" not in keywords:
+        raise NotThisMethod("Short version file found")
     date = keywords.get("date")
     if date is not None:
+        # Use only the last line.  Previous lines may contain GPG signature
+        # information.
+        date = date.splitlines()[-1]
+
         # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
         # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
         # -like" string, which we must then edit to make compliant), because
@@ -177,11 +178,11 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         if verbose:
             print("keywords are unexpanded, not using")
         raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
-    refs = set([r.strip() for r in refnames.strip("()").split(",")])
+    refs = {r.strip() for r in refnames.strip("()").split(",")}
     # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
     # just "foo-1.0". If we see a "tag: " prefix, prefer those.
     TAG = "tag: "
-    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
+    tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
     if not tags:
         # Either we're using git < 1.8.3, or there really are no tags. We use
         # a heuristic: assume all version tags have a digit. The old git %d
@@ -190,7 +191,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         # between branches and tags. By ignoring refnames without digits, we
         # filter out many common branch names like "release" and
         # "stabilization", as well as "HEAD" and "master".
-        tags = set([r for r in refs if re.search(r'\d', r)])
+        tags = {r for r in refs if re.search(r'\d', r)}
         if verbose:
             print("discarding '%s', no digits" % ",".join(refs - tags))
     if verbose:
@@ -199,6 +200,11 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         # sorting will prefer e.g. "2.0" over "2.0rc1"
         if ref.startswith(tag_prefix):
             r = ref[len(tag_prefix):]
+            # Filter out refs that exactly match prefix or that don't start
+            # with a number once the prefix is stripped (mostly a concern
+            # when prefix is '')
+            if not re.match(r'\d', r):
+                continue
             if verbose:
                 print("picking %s" % r)
             return {"version": r,
@@ -214,7 +220,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
 
 
 @register_vcs_handler("git", "pieces_from_vcs")
-def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
+def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
     """Get version from 'git describe' in the root of the source tree.
 
     This only gets called if the git-archive 'subst' keywords were *not*
@@ -225,8 +231,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     if sys.platform == "win32":
         GITS = ["git.cmd", "git.exe"]
 
-    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
-                          hide_stderr=True)
+    _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
+                   hide_stderr=True)
     if rc != 0:
         if verbose:
             print("Directory %s not under git control" % root)
@@ -234,15 +240,15 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
 
     # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
     # if there isn't one, this yields HEX[-dirty] (no NUM)
-    describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
-                                          "--always", "--long",
-                                          "--match", "%s*" % tag_prefix],
-                                   cwd=root)
+    describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty",
+                                     "--always", "--long",
+                                     "--match", "%s*" % tag_prefix],
+                              cwd=root)
     # --long was added in git-1.5.5
     if describe_out is None:
         raise NotThisMethod("'git describe' failed")
     describe_out = describe_out.strip()
-    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
+    full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
     if full_out is None:
         raise NotThisMethod("'git rev-parse' failed")
     full_out = full_out.strip()
@@ -252,6 +258,39 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     pieces["short"] = full_out[:7]  # maybe improved later
     pieces["error"] = None
 
+    branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
+                             cwd=root)
+    # --abbrev-ref was added in git-1.6.3
+    if rc != 0 or branch_name is None:
+        raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
+    branch_name = branch_name.strip()
+
+    if branch_name == "HEAD":
+        # If we aren't exactly on a branch, pick a branch which represents
+        # the current commit. If all else fails, we are on a branchless
+        # commit.
+        branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
+        # --contains was added in git-1.5.4
+        if rc != 0 or branches is None:
+            raise NotThisMethod("'git branch --contains' returned error")
+        branches = branches.split("\n")
+
+        # Remove the first line if we're running detached
+        if "(" in branches[0]:
+            branches.pop(0)
+
+        # Strip off the leading "* " from the list of branches.
+        branches = [branch[2:] for branch in branches]
+        if "master" in branches:
+            branch_name = "master"
+        elif not branches:
+            branch_name = None
+        else:
+            # Pick the first branch that is returned. Good or bad.
+            branch_name = branches[0]
+
+    pieces["branch"] = branch_name
+
     # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
     # TAG might have hyphens.
     git_describe = describe_out
@@ -293,13 +332,14 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     else:
         # HEX: no tags
         pieces["closest-tag"] = None
-        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
-                                    cwd=root)
+        count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
         pieces["distance"] = int(count_out)  # total number of commits
 
     # commit date: see ISO-8601 comment in git_versions_from_keywords()
-    date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"],
-                       cwd=root)[0].strip()
+    date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
+    # Use only the last line.  Previous lines may contain GPG signature
+    # information.
+    date = date.splitlines()[-1]
     pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
 
     return pieces
@@ -337,19 +377,49 @@ def render_pep440(pieces):
     return rendered
 
 
+def render_pep440_branch(pieces):
+    """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
+
+    The ".dev0" means not master branch. Note that .dev0 sorts backwards
+    (a feature branch will appear "older" than the master branch).
+
+    Exceptions:
+    1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            if pieces["branch"] != "master":
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0"
+        if pieces["branch"] != "master":
+            rendered += ".dev0"
+        rendered += "+untagged.%d.g%s" % (pieces["distance"],
+                                          pieces["short"])
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
 def render_pep440_pre(pieces):
-    """TAG[.post.devDISTANCE] -- No -dirty.
+    """TAG[.post0.devDISTANCE] -- No -dirty.
 
     Exceptions:
-    1: no tags. 0.post.devDISTANCE
+    1: no tags. 0.post0.devDISTANCE
     """
     if pieces["closest-tag"]:
         rendered = pieces["closest-tag"]
         if pieces["distance"]:
-            rendered += ".post.dev%d" % pieces["distance"]
+            rendered += ".post0.dev%d" % pieces["distance"]
     else:
         # exception #1
-        rendered = "0.post.dev%d" % pieces["distance"]
+        rendered = "0.post0.dev%d" % pieces["distance"]
     return rendered
 
 
@@ -380,12 +450,41 @@ def render_pep440_post(pieces):
     return rendered
 
 
+def render_pep440_post_branch(pieces):
+    """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
+
+    The ".dev0" means not master branch.
+
+    Exceptions:
+    1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%d" % pieces["distance"]
+            if pieces["branch"] != "master":
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "g%s" % pieces["short"]
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0.post%d" % pieces["distance"]
+        if pieces["branch"] != "master":
+            rendered += ".dev0"
+        rendered += "+g%s" % pieces["short"]
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
 def render_pep440_old(pieces):
     """TAG[.postDISTANCE[.dev0]] .
 
     The ".dev0" means dirty.
 
-    Eexceptions:
+    Exceptions:
     1: no tags. 0.postDISTANCE[.dev0]
     """
     if pieces["closest-tag"]:
@@ -456,10 +555,14 @@ def render(pieces, style):
 
     if style == "pep440":
         rendered = render_pep440(pieces)
+    elif style == "pep440-branch":
+        rendered = render_pep440_branch(pieces)
     elif style == "pep440-pre":
         rendered = render_pep440_pre(pieces)
     elif style == "pep440-post":
         rendered = render_pep440_post(pieces)
+    elif style == "pep440-post-branch":
+        rendered = render_pep440_post_branch(pieces)
     elif style == "pep440-old":
         rendered = render_pep440_old(pieces)
     elif style == "git-describe":
@@ -495,7 +598,7 @@ def get_versions():
         # versionfile_source is the relative path from the top of the source
         # tree (where the .git directory might live) to this file. Invert
         # this to find the root from __file__.
-        for i in cfg.versionfile_source.split('/'):
+        for _ in cfg.versionfile_source.split('/'):
             root = os.path.dirname(root)
     except NameError:
         return {"version": "0+unknown", "full-revisionid": None,

+ 0 - 1
dataladmetadatamodel/version.py

@@ -1 +0,0 @@
-__version__ = '0.1.0b5'

+ 6 - 6
setup.cfg

@@ -1,5 +1,7 @@
 [metadata]
-url = https://github.com/datalad/git-annex-ria-remote
+name = datalad-metadata-model
+version = 0.1.0b5
+url = https://github.com/datalad/datalad-metadata-model
 author = The DataLad Team and Contributors
 author_email = team@datalad.org
 description = Metadata model used in DataLad's extension for semantic metadata handling
@@ -8,15 +10,13 @@ long_description_content_type = text/markdown; charset=UTF-8; variant=GFM
 license = MIT
 classifiers =
     Programming Language :: Python
-    License :: OSI Approved :: BSD License
+    License :: OSI Approved :: MIT License
     Programming Language :: Python :: 3
 
 [options]
-python_requires = >= 3.5
+python_requires = >= 3.6
 install_requires =
-    datalad >= 0.14
-    datalad-metadata-model
-    pyyaml
+    datalad >= 0.15
 test_requires =
     nose
     coverage

+ 7 - 5
setup.py

@@ -1,13 +1,14 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-import os
-import sys
 import setuptools
+import sys
+from os.path import dirname
 
-print(os.getcwd())
-sys.path.insert(0, os.getcwd())
-print(sys.path)
+# This is needed for versioneer to be importable when building with PEP 517.
+# See <https://github.com/warner/python-versioneer/issues/193> and links
+# therein for more information.
+sys.path.append(dirname(__file__))
 import versioneer
 
 
@@ -18,6 +19,7 @@ with open("README.md", "r") as fh:
 setuptools.setup(
     name="datalad-metadata-model",
     version=versioneer.get_version(),
+    cmdclass=versioneer.get_cmdclass(),
     author="The Datalad Team",
     author_email="christian.moench@web.de",
     description="Datalad Metadata Model",

+ 0 - 291
simple_test/persisted_object.py

@@ -1,291 +0,0 @@
-import logging
-from typing import (
-    Dict,
-    Iterable,
-    List,
-    Optional,
-    Union
-)
-
-
-from dataladmetadatamodel.mapper.gitmapper.gitbackend.subprocess import (
-    git_load_json,
-    git_load_str,
-    git_save_json,
-    git_save_str
-)
-
-
-logging.basicConfig(level=logging.DEBUG)
-repo_dir: str = "/home/cristian/tmp/mapptest"
-
-
-SReference = str
-SJSON = Union[str, int, float, Dict, List]
-
-
-class Mapper:
-    def read_in(self, mappable_object: "MappableObject", SReference):
-        raise NotImplementedError
-
-    def write_out(self, mappable_object: "MappableObject") -> SReference:
-        raise NotImplementedError
-
-
-mapper: Dict[str, Mapper] = dict()
-
-
-class ModifiableObject:
-    """
-    Object that tracks modification status.
-
-    Responsibilities:
-     - allow touching and cleaning
-     - determine modification state based
-       on subclass-implementation of is_modified_impl()
-    """
-    def __init__(self):
-        # A modifiable object is assumed
-        # to be unmodified upon creation
-        self.dirty = False
-
-    def touch(self):
-        self.dirty = True
-
-    def clean(self):
-        self.dirty = False
-
-    def is_modified(self) -> bool:
-        """
-        Determine whether the object or one of its contained
-        objects was modified.
-        """
-        if not self.dirty:
-            self.dirty = self._sub_elements_modified()
-        return self.dirty
-
-    def _sub_elements_modified(self):
-        return any(map(lambda element: element.is_modified(), self.get_modifiable_mapped_sub_elements()))
-
-    def xxxsub_elements_modified(self) -> bool:
-        """
-        By default modification state is determined by
-        the dirty flag in this object, i.e. whether
-        self.clean() or self.touch() has been invoked
-        latest.
-
-        :return: True if any sub-element exists that is
-                 modified, else False
-        """
-        return False
-
-    def get_modifiable_mapped_sub_elements(self) -> Iterable:
-        return []
-
-
-class MappableObject(ModifiableObject):
-    """
-    Base class for objects that can
-    be mapped onto a storage backend
-    """
-    def __init__(self, reference: Optional[SReference]):
-        super().__init__()
-        self.reference = reference
-        if reference is None:
-            self.mapped = True
-        else:
-            self.mapped = False
-
-    def read_in(self):
-        if self.mapped:
-            return
-        assert self.reference is not None
-        mapper[type(self).__name__].read_in(self, self.reference)
-        self.mapped = True
-        self.clean()
-
-    def write_out(self) -> SReference:
-        if self.mapped:
-            self.reference = mapper[type(self).__name__].write_out(self)
-            self.clean()
-        assert self.reference is not None
-        return self.reference
-
-    def purge(self):
-        if self.mapped:
-            self.purge_impl()
-            self.mapped = False
-            self.clean()
-
-    def purge_impl(self):
-        raise NotImplementedError
-
-
-class MappableDict(MappableObject):
-    def __init__(self, reference: Optional[SReference] = None):
-        super().__init__(reference)
-        self.content = dict()
-
-    def put(self, key: str, value: SJSON):
-        self.content[key] = value
-        self.touch()
-
-    def get(self, key: str) -> SJSON:
-        return self.content[key]
-
-    def purge_impl(self):
-        self.content = dict()
-
-
-class ComplexDict(MappableObject):
-    def __init__(self, reference: Optional[SReference] = None):
-        super().__init__(reference)
-        self.content = dict()
-
-    def put(self, key: str, value: SJSON):
-        self.content[key] = value
-        self.touch()
-
-    def get(self, key: str) -> SJSON:
-        return self.content[key]
-
-    def purge_impl(self):
-        self.content = dict()
-
-
-class CTree(MappableObject):
-    def __init__(self, reference: Optional[SReference] = None):
-        super().__init__(reference)
-        self.tree = dict()
-
-    def put_c(self, path: str, mappable_dict: MappableDict):
-        self.tree[path] = mappable_dict
-        self.touch()
-
-    def get_c(self, path: str) -> MappableDict:
-        mappable_dict = self.tree[path]
-        mappable_dict.read_in()
-        return mappable_dict
-
-    def purge_impl(self):
-        self.tree = dict()
-
-    def get_modifiable_mapped_sub_elements(self) -> Iterable:
-        yield from self.tree.values()
-
-
-class MappedDictMapper(Mapper):
-
-    def read_in(self, mappable_object: MappableDict, reference: SReference):
-        mappable_object.content = git_load_json(repo_dir, str(reference))
-
-    def write_out(self, mappable_object: MappableDict) -> SReference:
-        return git_save_json(repo_dir, mappable_object.content)
-
-
-class ComplexDictMapper(Mapper):
-
-    def read_in(self, complex_dict: ComplexDict, reference: SReference):
-        first_level = git_load_json(repo_dir, str(reference))
-        for key, value in first_level.items():
-            complex_dict.content[key] = git_load_str(repo_dir, first_level[key])
-
-    def write_out(self, complex_dict: ComplexDict) -> SReference:
-        first_level = {
-            key: git_save_str(repo_dir, value)
-            for key, value in complex_dict.content.items()
-        }
-        return git_save_json(repo_dir, first_level)
-
-
-class CTreeMapper(Mapper):
-
-    def read_in(self, c_tree: CTree, reference: SReference):
-        first_level = git_load_json(repo_dir, str(reference))
-        logging.debug(f"CTreeMapper: read_in: first_level: {first_level}")
-        for key, value in first_level.items():
-            c_tree.tree[key] = MappableDict(git_load_str(repo_dir, first_level[key])[5:])
-
-    def write_out(self, c_tree: CTree) -> SReference:
-        first_level = {
-            key: git_save_str(repo_dir, "link:" + mapped_dict.write_out())
-            for key, mapped_dict in c_tree.tree.items()
-        }
-        logging.debug(f"CTreeMapper: write_out: first_level: {first_level}")
-        return git_save_json(repo_dir, first_level)
-
-
-mapper["MappableDict"] = MappedDictMapper()
-mapper["ComplexDict"] = ComplexDictMapper()
-mapper["CTree"] = CTreeMapper()
-
-
-def test():
-
-    ct = CTree()
-    for path in ["a/b/c1", "a/b/c2", "a/b/c3"]:
-        sub_elment = MappableDict()
-        for ext in ("-v0", "-v1", "-v2"):
-            sub_elment.put(path + ext, "This is value for: " + path + ext)
-        ct.put_c(path, sub_elment)
-
-    print(ct.is_modified())
-
-    reference = ct.write_out()
-    print(reference)
-
-    print(ct.is_modified())
-
-    md = ct.get_c("a/b/c1")
-    md.put("test-mod", "a key to test modification")
-
-    print(ct.is_modified())
-
-    reference = ct.write_out()
-    print(reference)
-
-    ct.purge()
-
-    ct2 = CTree(reference)
-    ct2.read_in()
-
-    print(ct2)
-
-    return
-
-    cd = ComplexDict()
-    for key, value in (("a", "this is a"),
-                       ("b", "bbbb is here!")):
-        cd.put(key, value)
-
-    print(cd.is_modified())
-
-    reference = cd.write_out()
-    print(reference)
-
-    cd.purge()
-    cd.read_in()
-    print(cd.content)
-
-    return
-    md = MappableDict()
-    for i in range(32):
-        md.put(str(i), hex(i))
-
-    print(md.is_modified())
-
-    reference = md.write_out()
-    print(reference)
-
-    md.purge()
-    md.read_in()
-    print(md.content)
-
-
-    md2 = MappableDict(reference)
-    md2.read_in()
-    print(md2.content)
-
-
-if __name__ == "__main__":
-    test()

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 438 - 196
versioneer.py