From 7525dfca0af23868dbf486e11aff8cac63080cbd Mon Sep 17 00:00:00 2001 From: snajder Date: Fri, 14 May 2021 14:48:08 +0200 Subject: [PATCH 1/5] List replacements --- README.md | 16 ++--- meta.yaml | 7 +- setup.py | 12 +++- versipy.yaml | 22 +++--- versipy/__init__.py | 2 +- versipy/common.py | 137 ++++++++++++++++++++++-------------- versipy_history.txt | 12 ++++ versipy_templates/README.md | 12 ++-- versipy_templates/meta.yaml | 9 ++- versipy_templates/setup.py | 6 +- 10 files changed, 142 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index fc47b51..41f0e0b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# versipy v0.2.4.post1 +# versipy v0.2.5 ![](pictures/versipy.png) @@ -65,9 +65,9 @@ conda update -c aleg -c anaconda -c bioconda -c conda-forge versipy The following dependencies are required but automatically installed with pip or conda package manager -- colorlog>=4.1.0 -- pyyaml>=5.3.1 -- gitpython>=3.1.9 + - colorlog>=4.1.0 + - pyyaml>=5.3.1 + - gitpython>=3.1.9 ## Usage @@ -183,10 +183,10 @@ deploy: ## Classifiers -* Development Status :: 3 - Alpha -* Intended Audience :: Science/Research -* Topic :: Scientific/Engineering :: Bio-Informatics -* License :: OSI Approved :: GNU General Public License v3 (GPLv3) +* Development Status :: 3 - Alpha +* Intended Audience :: Science/Research +* Topic :: Scientific/Engineering :: Bio-Informatics +* License :: OSI Approved :: GNU General Public License v3 (GPLv3) * Programming Language :: Python :: 3 ## citation diff --git a/meta.yaml b/meta.yaml index 3127299..6c02795 100644 --- a/meta.yaml +++ b/meta.yaml @@ -1,4 +1,4 @@ -{% set version = "0.2.4.post1" %} +{% set version = "0.2.5" %} {% set name = "versipy" %} package: @@ -19,8 +19,9 @@ build: requirements: build: - python>=3.6 - - pip>=19.2.1 - - ripgrep>=11.0.1 + - colorlog>=4.1.0 + - pyyaml>=5.3.1 + - gitpython>=3.1.9 run: - colorlog>=4.1.0 - pyyaml>=5.3.1 diff --git a/setup.py b/setup.py index 89b6d82..bf95757 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name="versipy", description="Versatile version and medatada managment across the python packaging ecosystem with git integration", - version="0.2.4.post1", + version="0.2.5", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/a-slide/versipy", @@ -19,8 +19,14 @@ author_email="contact@adrienleger.com", license="GPLv3", python_requires=">=3.6", - classifiers=["Development Status :: 3 - Alpha", "Intended Audience :: Science/Research", "Topic :: Scientific/Engineering :: Bio-Informatics", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Programming Language :: Python :: 3"], - install_requires=["colorlog>=4.1.0", "pyyaml>=5.3.1", "gitpython>=3.1.9"], + classifiers=["Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Bio-Informatics", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Programming Language :: Python :: 3" + install_requires=["colorlog>=4.1.0", + "pyyaml>=5.3.1", + "gitpython>=3.1.9"], packages=["versipy"], package_dir={"versipy": "versipy"}, package_data={"versipy": ["templates/*"]}, diff --git a/versipy.yaml b/versipy.yaml index 9411b3a..d21b2a3 100644 --- a/versipy.yaml +++ b/versipy.yaml @@ -1,11 +1,11 @@ version: major: 0 minor: 2 - micro: 4 + micro: 5 a: null b: null rc: null - post: 1 + post: null dev: null managed_values: __package_name__: versipy @@ -19,14 +19,16 @@ managed_values: __package_licence_url__: https://www.gnu.org/licenses/gpl-3.0.en.html __minimal_python__: '>=3.6' __entry_point1__: versipy=versipy.__main__:main - __dependency1__: colorlog>=4.1.0 - __dependency2__: pyyaml>=5.3.1 - __dependency3__: gitpython>=3.1.9 - __classifiers_1__: 'Development Status :: 3 - Alpha' - __classifiers_2__: 'Intended Audience :: Science/Research' - __classifiers_3__: 'Topic :: Scientific/Engineering :: Bio-Informatics' - __classifiers_4__: 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)' - __classifiers_5__: 'Programming Language :: Python :: 3' + __dependencies__: + - colorlog>=4.1.0 + - pyyaml>=5.3.1 + - gitpython>=3.1.9 + __classifiers__: + - 'Development Status :: 3 - Alpha' + - 'Intended Audience :: Science/Research' + - 'Topic :: Scientific/Engineering :: Bio-Informatics' + - 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)' + - 'Programming Language :: Python :: 3' __citation__: Adrien Leger. (2020, October 27). a-slide/versipy 0.2.2 (Version 0.2.2). Zenodo. http://doi.org/10.5281/zenodo.4139248 managed_files: diff --git a/versipy/__init__.py b/versipy/__init__.py index d8b950e..3c42a14 100644 --- a/versipy/__init__.py +++ b/versipy/__init__.py @@ -1,5 +1,5 @@ __name__ = "versipy" -__version__ = "0.2.4.post1" +__version__ = "0.2.5" __description__ = "Versatile version and medatada managment across the python packaging ecosystem with git integration" __url__ = "https://github.com/a-slide/versipy" __licence__ = "GPLv3" diff --git a/versipy/common.py b/versipy/common.py index 8e4fe44..1c5e122 100644 --- a/versipy/common.py +++ b/versipy/common.py @@ -85,7 +85,7 @@ def choose_option(choices=["y", "n"], message="Choose a valid option"): def get_logger(name=None, verbose=False, quiet=False): """Multilevel colored log using colorlog""" - + # Define conditional color formatter formatter = colorlog.LevelFormatter( fmt={ @@ -104,12 +104,12 @@ def get_logger(name=None, verbose=False, quiet=False): }, reset=True, ) - + # Define logger with custom formatter logging.basicConfig(format="%(message)s") logging.getLogger().handlers[0].setFormatter(formatter) log = logging.getLogger(name) - + # Define logging level depending on verbosity if verbose: log.setLevel(logging.DEBUG) @@ -117,7 +117,7 @@ def get_logger(name=None, verbose=False, quiet=False): log.setLevel(logging.WARNING) else: log.setLevel(logging.INFO) - + return log @@ -150,10 +150,10 @@ def log_list(l, logger, header="", indent="\t"): def doc_func(func): """Parse the function description string""" - + if inspect.isclass(func): func = func.__init__ - + docstr_list = [] for l in inspect.getdoc(func).split("\n"): l = l.strip() @@ -162,17 +162,17 @@ def doc_func(func): break else: docstr_list.append(l) - + return " ".join(docstr_list) def make_arg_dict(func): """Parse the arguments default value, type and doc""" - + # Init method for classes if inspect.isclass(func): func = func.__init__ - + if inspect.isfunction(func) or inspect.ismethod(func): # Parse arguments default values and annotations d = OrderedDict() @@ -188,7 +188,7 @@ def make_arg_dict(func): d[name]["required"] = True else: d[name]["default"] = p.default - + # Parse the docstring in a dict docstr_dict = OrderedDict() lab = None @@ -200,7 +200,7 @@ def make_arg_dict(func): docstr_dict[lab] = [] elif lab: docstr_dict[lab].append(l) - + # Concatenate and copy doc in main dict for name in d.keys(): if name in docstr_dict: @@ -210,12 +210,12 @@ def make_arg_dict(func): def arg_from_docstr(parser, func, arg_name, short_name=None): """Get options corresponding to argument name from docstring and deal with special cases""" - + if short_name: arg_names = ["-{}".format(short_name), "--{}".format(arg_name)] else: arg_names = ["--{}".format(arg_name)] - + arg_dict = make_arg_dict(func)[arg_name] if "help" in arg_dict: if "default" in arg_dict: @@ -225,13 +225,13 @@ def arg_from_docstr(parser, func, arg_name, short_name=None): arg_dict["help"] += " (default: %(default)s)" else: arg_dict["help"] += " (required)" - + if "type" in arg_dict: if arg_dict["type"] == bool: arg_dict["help"] += " [boolean]" else: arg_dict["help"] += " [%(type)s]" - + # Special case for boolean args if arg_dict["type"] == bool: if arg_dict["default"] == False: @@ -240,12 +240,12 @@ def arg_from_docstr(parser, func, arg_name, short_name=None): elif arg_dict["default"] == True: arg_dict["action"] = "store_false" del arg_dict["type"] - + # Special case for lists args elif isinstance(arg_dict["type"], list): arg_dict["nargs"] = "*" arg_dict["type"] = arg_dict["type"][0] - + parser.add_argument(*arg_names, **arg_dict) @@ -259,11 +259,11 @@ def ordered_load_yaml(yaml_fn, Loader=yaml.Loader, **kwargs): # Define custom loader class OrderedLoader(Loader): pass - + def construct_mapping(loader, node): loader.flatten_mapping(node) return OrderedDict(loader.construct_pairs(node)) - + OrderedLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping) # Try to load file try: @@ -281,12 +281,12 @@ def ordered_dump_yaml(d, yaml_fn, Dumper=yaml.Dumper, **kwargs): # Define custom dumper class OrderedDumper(Dumper): pass - + def _dict_representer(dumper, data): return dumper.represent_mapping(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, data.items()) - + OrderedDumper.add_representer(OrderedDict, _dict_representer) - + # Try to dump dict to file try: with open(yaml_fn, "w") as yaml_fp: @@ -330,11 +330,11 @@ def get_version_str(d): def get_versipy_yaml(versipy_fn, log): """load end check versipy file""" - + # Try to load YAML file log.debug("Loading versipy YAML file") info_d = ordered_load_yaml(versipy_fn) - + # Check that all fields are there log.debug("Checking file structure") for field in ["version", "managed_values", "managed_files"]: @@ -342,7 +342,7 @@ def get_versipy_yaml(versipy_fn, log): raise ValueError("Missing section '{}' in versipy YAML file".format(field)) if not info_d[field]: raise ValueError("Empty section '{}' in versipy YAML file".format(field)) - + # Verify validity of version string log.debug("Checking version") for field in ["major", "minor", "micro", "a", "b", "rc", "post", "dev"]: @@ -351,7 +351,7 @@ def get_versipy_yaml(versipy_fn, log): version_str = get_version_str(info_d["version"]) if not is_canonical_version(version_str): raise ValueError("Current version {} is not a valid PEP canonical version".format(version_str)) - + return info_d @@ -378,11 +378,11 @@ def increment_version( post=False, dev=False, ): - + # safe increment variable even if None def increment_safe(v): return 1 if v is None else v + 1 - + version_d = copy.deepcopy(version_d) if major: log.debug("Increment major level and reset all lower levels") @@ -392,13 +392,13 @@ def increment_safe(v): log.debug("Increment minor level and reset all lower levels") version_d["minor"] = increment_safe(version_d["minor"]) version_d = reset_version(version_d, levels=["micro", "a", "b", "rc", "post", "dev"]) - + # optional micro version number if micro: log.debug("Increment micro level and reset all lower levels") version_d["micro"] = increment_safe(version_d["micro"]) version_d = reset_version(version_d, levels=["a", "b", "rc", "post", "dev"]) - + # optional release type version if rc: log.debug("Increment rc level and reset all lower levels") @@ -412,7 +412,7 @@ def increment_safe(v): log.debug("Increment a level and reset all lower levels") version_d["a"] = increment_safe(version_d["a"]) version_d = reset_version(version_d, levels=["b", "rc", "post", "dev"]) - + # optional post and dev tags if post: log.debug("Increment post level and reset all lower levels") @@ -420,12 +420,12 @@ def increment_safe(v): if dev: log.debug("Increment dev level and reset all lower levels") version_d["dev"] = increment_safe(version_d["dev"]) - + # sanity check version_str = get_version_str(version_d) if not is_canonical_version(version_str): raise ValueError("Current version {version_str} is not a valid PEP canonical version") - + log_dict(version_d, log.debug, "Updated version values") return version_d @@ -434,7 +434,7 @@ def parse_version_str(version_str, log): """""" if not is_canonical_version(version_str): raise ValueError("Current version {version_str} is not a valid PEP canonical version") - + log.debug("Split version number into a list") alphabet = list(string.ascii_letters) l = [] @@ -452,7 +452,7 @@ def parse_version_str(version_str, log): elif c.isdigit(): s += c l.append(s) - + log.debug("Store list values in version dictionary") version_d = OrderedDict(major=0, minor=None, micro=None, a=None, b=None, rc=None, post=None, dev=None) if l[0].isdigit(): @@ -466,7 +466,7 @@ def parse_version_str(version_str, log): if e.startswith(tag): version_d[tag] = int(e.strip(tag)) break - + log_dict(version_d, log.debug, "Updated version values") return version_d @@ -474,20 +474,20 @@ def parse_version_str(version_str, log): def update_managed_files(info_d, overwrite, dry, log): """""" version_str = get_version_str(info_d["version"]) - + for src_fn, dest_fn in info_d["managed_files"].items(): log.debug("Updating file {}".format(dest_fn)) - + # Bulletproof reading and writing try: src_fp = dest_fp = None - + # Open template file for reading try: src_fp = open(src_fn, "r") except: raise IOError("Cannot read source Template file: {}".format(src_fn)) - + # Open destination file for writing if not dry: try: @@ -501,17 +501,48 @@ def update_managed_files(info_d, overwrite, dry, log): dest_fp = open(dest_fn, "w") except: raise IOError("Cannot write to destination file: {}".format(dest_fn)) - + s = src_fp.read() s = s.replace("__package_version__", version_str) for k, v in info_d["managed_values"].items(): - s = s.replace(k, v) - + if isinstance(v, list): + key_name = k[2:-2] + """ + Let me explain the ugliest regex in the world. Note that curly braces are doubled for escaping + __@{{ Literal + ((?!::)[^}}]*) Not :: and not } + :: Literal + (((?!{key_name}).)*) Not key_name + {key_name} Literally key_name + ([^}}]*) Not curly brace + }}__ Literal + + So it looks for a string built like: + __@{::key_name}__ + and will replace it with: + value1value2value3 + + The regex works for multiple ocurrences. + + For example, assuming key_name is "dependencies" and our dependencies are "numpy", "meth5" and "pandas", + and it finds something like: + __@{, ::"dependencies"}__ + it will be replaced with: + "numpy", "meth5", "pandas" + """ + s = re.sub( + f"__@{{((?!::)[^}}]*)::(((?!{key_name}).)*){key_name}([^}}]*)}}__", + "\\1".join([f"\\2{vi}\\3" for vi in v]), + s, + ) + else: + s = s.replace(k, v) + if dry: stdout_print(s) else: dest_fp.write(s) - + finally: # Try to close file pointers for fp, fn in [[src_fp, src_fn], [dest_fp, dest_fn]]: @@ -541,7 +572,7 @@ def update_versipy_files(info_d, versipy_fn, versipy_history_fn, comment, overwr def get_versipy_yaml_template(): info_d = OrderedDict() - + # Version section info_d["version"] = OrderedDict() info_d["version"]["major"] = 0 @@ -552,7 +583,7 @@ def get_versipy_yaml_template(): info_d["version"]["rc"] = None info_d["version"]["post"] = None info_d["version"]["dev"] = None - + # Managed values section info_d["managed_values"] = OrderedDict() info_d["managed_values"]["__package_name__"] = "package name" @@ -561,22 +592,22 @@ def get_versipy_yaml_template(): info_d["managed_values"]["__package_licence__"] = "package licence" info_d["managed_values"]["__author_name__"] = "author name" info_d["managed_values"]["__author_email__"] = "author contact email" - + # Managed files section info_d["managed_files"] = OrderedDict() info_d["managed_files"]["versipy_templates/setup.py"] = "setup.py" info_d["managed_files"]["versipy_templates/meta.yaml"] = "meta.yaml" info_d["managed_files"]["versipy_templates/__init__.py"] = "versipy/__init__.py" info_d["managed_files"]["versipy_templates/README.md"] = "README.md" - + return info_d def write_versipy_yaml(versipy_fn, overwrite, log): - + info_d = versipy_info_d() log_dict(info_d, log.debug, "template info dict") - + # Open destination file for writing log.debug("Try to dump data to YAML file {}".format(versipy_fn)) if not overwrite and os.path.isfile(versipy_fn): @@ -593,18 +624,18 @@ def git_files(files, version, comment, git_tag, log): log.debug("Acquire local repository") repo = Repo() remote = repo.remote("origin") - + log.debug("Add, commit and push version files") for f in files: repo.index.add(f) commit = repo.index.commit(message=comment) push = remote.push() - + if git_tag: log.debug("Set and push new version tag") tag = repo.create_tag(version, message=comment) push = remote.push(tag) - + except Exception as E: log.info("Failed to push to remote") log.debug(type(E), str(E)) diff --git a/versipy_history.txt b/versipy_history.txt index 0896412..5580498 100644 --- a/versipy_history.txt +++ b/versipy_history.txt @@ -16,3 +16,15 @@ 2020-10-27 16:29:35.809467 0.2.3.dev2 Update doc and formatting changes 2020-10-27 16:34:43.746265 0.2.4 Update doc and formatting changes 2020-10-27 18:28:53.873189 0.2.4.post1 typo fix +2021-05-14 14:33:22.566348 0.2.4.post1.dev1 Versipy auto bump-up +2021-05-14 14:34:34.599597 0.2.4.post1.dev2 Versipy auto bump-up +2021-05-14 14:34:59.852766 0.2.4.post1.dev3 Versipy auto bump-up +2021-05-14 14:41:40.568131 0.2.4.post1.dev4 Versipy auto bump-up +2021-05-14 14:42:32.820918 0.2.4.post1.dev5 Versipy auto bump-up +2021-05-14 14:42:55.346348 0.2.4.post1.dev6 Versipy auto bump-up +2021-05-14 14:43:29.700489 0.2.4.post1.dev7 Versipy auto bump-up +2021-05-14 14:43:46.163946 0.2.4.post1.dev8 Versipy auto bump-up +2021-05-14 14:44:38.270605 0.2.4.post1.dev9 Versipy auto bump-up +2021-05-14 14:46:00.410942 0.2.4.post1.dev10 Versipy auto bump-up +2021-05-14 14:46:48.540644 0.2.4.post1.dev11 Versipy auto bump-up +2021-05-14 14:47:48.960540 0.2.5 Versipy auto bump-up diff --git a/versipy_templates/README.md b/versipy_templates/README.md index 8cb96fd..eb44200 100644 --- a/versipy_templates/README.md +++ b/versipy_templates/README.md @@ -65,9 +65,8 @@ conda update -c aleg -c anaconda -c bioconda -c conda-forge versipy The following dependencies are required but automatically installed with pip or conda package manager -- __dependency1__ -- __dependency2__ -- __dependency3__ + - __@{ + - ::dependencies}__ ## Usage @@ -183,11 +182,8 @@ deploy: ## Classifiers -* __classifiers_1__ -* __classifiers_2__ -* __classifiers_3__ -* __classifiers_4__ -* __classifiers_5__ +* __@{ +* ::classifiers}__ ## citation diff --git a/versipy_templates/meta.yaml b/versipy_templates/meta.yaml index d5f4683..887f23a 100644 --- a/versipy_templates/meta.yaml +++ b/versipy_templates/meta.yaml @@ -19,12 +19,11 @@ build: requirements: build: - python__minimal_python__ - - pip>=19.2.1 - - ripgrep>=11.0.1 + - __@{ + - ::dependencies}__ run: - - __dependency1__ - - __dependency2__ - - __dependency3__ + - __@{ + - ::dependencies}__ about: home: __package_url__ license: __package_licence__ diff --git a/versipy_templates/setup.py b/versipy_templates/setup.py index 72c3342..3b697de 100644 --- a/versipy_templates/setup.py +++ b/versipy_templates/setup.py @@ -19,8 +19,10 @@ author_email="__author_email__", license="__package_licence__", python_requires="__minimal_python__", - classifiers=["__classifiers_1__", "__classifiers_2__", "__classifiers_3__", "__classifiers_4__", "__classifiers_5__"], - install_requires=["__dependency1__", "__dependency2__", "__dependency3__"], + classifiers=[__@{, + ::"classifiers"}__ + install_requires=[__@{, + ::"dependencies"}__], packages=["__package_name__"], package_dir={"__package_name__": "__package_name__"}, package_data={"__package_name__": ["templates/*"]}, From ea4ea12d952f34ed80aee9465ed655183db5d375 Mon Sep 17 00:00:00 2001 From: snajder Date: Fri, 14 May 2021 15:19:14 +0200 Subject: [PATCH 2/5] Fixed erroneously replaced build requirements --- meta.yaml | 5 ++--- versipy_templates/meta.yaml | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/meta.yaml b/meta.yaml index 6c02795..04fee59 100644 --- a/meta.yaml +++ b/meta.yaml @@ -19,9 +19,8 @@ build: requirements: build: - python>=3.6 - - colorlog>=4.1.0 - - pyyaml>=5.3.1 - - gitpython>=3.1.9 + - pip>=19.2.1 + - ripgrep>=11.0.1 run: - colorlog>=4.1.0 - pyyaml>=5.3.1 diff --git a/versipy_templates/meta.yaml b/versipy_templates/meta.yaml index 887f23a..8d2666f 100644 --- a/versipy_templates/meta.yaml +++ b/versipy_templates/meta.yaml @@ -19,8 +19,8 @@ build: requirements: build: - python__minimal_python__ - - __@{ - - ::dependencies}__ + - pip>=19.2.1 + - ripgrep>=11.0.1 run: - __@{ - ::dependencies}__ From eed0737b72b6e081523a28e014772d4cd9d57a34 Mon Sep 17 00:00:00 2001 From: snajder Date: Fri, 14 May 2021 15:38:19 +0200 Subject: [PATCH 3/5] Improved comment explaining the regex --- versipy/common.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/versipy/common.py b/versipy/common.py index 1c5e122..ff2fff1 100644 --- a/versipy/common.py +++ b/versipy/common.py @@ -510,17 +510,17 @@ def update_managed_files(info_d, overwrite, dry, log): """ Let me explain the ugliest regex in the world. Note that curly braces are doubled for escaping __@{{ Literal - ((?!::)[^}}]*) Not :: and not } + ((?!::)[^}}]*) Not :: and not } -> captured as group 1 (separator) :: Literal - (((?!{key_name}).)*) Not key_name + (((?!{key_name}).)*) Not key_name -> captured as group 2 (prefix) {key_name} Literally key_name - ([^}}]*) Not curly brace + ([^}}]*) Not curly brace -> captured as group 3 (suffix) }}__ Literal So it looks for a string built like: - __@{::key_name}__ + __@{::
key_name}__
                     and will replace it with:
-                    value1value2value3
+                    
value1
value2
value3
                     
                     The regex works for multiple ocurrences.
                     

From 832eebbd71e016e1d96f7e158d68254bce21ee03 Mon Sep 17 00:00:00 2001
From: snajder 
Date: Mon, 17 May 2021 11:52:39 +0200
Subject: [PATCH 4/5] Updated readme

---
 versipy_templates/README.md | 50 +++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/versipy_templates/README.md b/versipy_templates/README.md
index eb44200..48dd29e 100644
--- a/versipy_templates/README.md
+++ b/versipy_templates/README.md
@@ -178,8 +178,58 @@ deploy:
       tags: true
 ```
 
+### Lists
+
+In addition to simple string replacement, `versipy` offers some advanced syntax for the support of lists (such as 
+dependencies or classifiers). This syntax allows to some flexibility in formatting lists depending on the template.
+
+If a managed variable in `versipy.yaml` contains a list, the template needs to specify the formatting as such:
+
+`__@{::}`__
+
+For example, if `versipy.yaml` contains this entry:
+
+```yaml
+managed_values:
+  __dependencies__:
+  - numpy
+  - pandas
+  - matplotlib
+```
+
+And the template for `setup.py` contains the following line:
+```python
+    install_requires=[__@{, ::"dependencies"}__],
+```
+
+then versipy will interpret `, ` as the list separator, and `"` as both prefix and suffix. The resulting line will then be:
+
+```python
+    install_requires=["numpy", "pandas", "matplotlib"],
+```
+
+Wheras in `meta.yaml` you might define in the template:
+```yaml
+  run:
+  - __@{
+  - ::dependencies}__
+```
+
+where `versipy` interprets `\n  - ` as the separator, and the prefix and suffix are empty strings. The resulting `meta.yaml`
+will then contain:
+
+```yaml
+  run:
+  - numpy
+  - pandas
+  - matplotlib   
+```
+
+
 ---
 
+
+
 ## Classifiers
 
 * __@{ 

From 812645a9f7d2e4e894c775209d58924a09824281 Mon Sep 17 00:00:00 2001
From: snajder 
Date: Mon, 17 May 2021 11:58:41 +0200
Subject: [PATCH 5/5] Fixed bug in setup.py template, and generated readme

---
 README.md                  | 51 ++++++++++++++++++++++++++++++++++++++
 setup.py                   |  2 +-
 versipy_history.txt        |  1 +
 versipy_templates/setup.py |  2 +-
 4 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 41f0e0b..66d6047 100644
--- a/README.md
+++ b/README.md
@@ -179,8 +179,59 @@ deploy:
       tags: true
 ```
 
+### Lists
+
+In addition to simple string replacement, `versipy` offers some advanced syntax for the support of lists (such as 
+dependencies or classifiers). This syntax allows to some flexibility in formatting lists depending on the template.
+
+If a managed variable in `versipy.yaml` contains a list, the template needs to specify the formatting as such:
+
+`__@{::}`__
+
+For example, if `versipy.yaml` contains this entry:
+
+```yaml
+managed_values:
+  __dependencies__:
+  - numpy
+  - pandas
+  - matplotlib
+```
+
+And the template for `setup.py` contains the following line:
+```python
+    install_requires=["colorlog>=4.1.0", "pyyaml>=5.3.1", "gitpython>=3.1.9"],
+```
+
+then versipy will interpret `, ` as the list separator, and `"` as both prefix and suffix. The resulting line will then be:
+
+```python
+    install_requires=["numpy", "pandas", "matplotlib"],
+```
+
+Wheras in `meta.yaml` you might define in the template:
+```yaml
+  run:
+  - colorlog>=4.1.0
+  - pyyaml>=5.3.1
+  - gitpython>=3.1.9
+```
+
+where `versipy` interprets `\n  - ` as the separator, and the prefix and suffix are empty strings. The resulting `meta.yaml`
+will then contain:
+
+```yaml
+  run:
+  - numpy
+  - pandas
+  - matplotlib   
+```
+
+
 ---
 
+
+
 ## Classifiers
 
 * Development Status :: 3 - Alpha 
diff --git a/setup.py b/setup.py
index bf95757..00c4a66 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@
         "Intended Audience :: Science/Research",
         "Topic :: Scientific/Engineering :: Bio-Informatics",
         "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
-        "Programming Language :: Python :: 3"
+        "Programming Language :: Python :: 3"],
     install_requires=["colorlog>=4.1.0",
         "pyyaml>=5.3.1",
         "gitpython>=3.1.9"],
diff --git a/versipy_history.txt b/versipy_history.txt
index 5580498..3a25973 100644
--- a/versipy_history.txt
+++ b/versipy_history.txt
@@ -28,3 +28,4 @@
 2021-05-14 14:46:00.410942	0.2.4.post1.dev10	Versipy auto bump-up
 2021-05-14 14:46:48.540644	0.2.4.post1.dev11	Versipy auto bump-up
 2021-05-14 14:47:48.960540	0.2.5	Versipy auto bump-up
+2021-05-17 11:58:08.278705	0.2.5	Manually set version
diff --git a/versipy_templates/setup.py b/versipy_templates/setup.py
index 3b697de..4f6a72c 100644
--- a/versipy_templates/setup.py
+++ b/versipy_templates/setup.py
@@ -20,7 +20,7 @@
     license="__package_licence__",
     python_requires="__minimal_python__",
     classifiers=[__@{,
-        ::"classifiers"}__
+        ::"classifiers"}__],
     install_requires=[__@{,
         ::"dependencies"}__],
     packages=["__package_name__"],