Skip to content

Commit

Permalink
refactor: use TemplateList ror TextFile
Browse files Browse the repository at this point in the history
  • Loading branch information
fblanchetNaN committed Apr 17, 2022
1 parent bb711af commit 0a80f69
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 115 deletions.
10 changes: 4 additions & 6 deletions incipyt/_internal/dumpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,13 @@ def dump_in(self, config):


class TextFile(BaseDumper):
def dump_in(self, config):
with self.open() as file:
file.write(config[None])

def __init__(self, path, sep="\n", sanitizer=None):
super().__init__(path, sanitizer)
self._sep = sep

class Requirement(BaseDumper):
def dump_in(self, config):
with self.open() as file:
file.write("\n".join(config[None]))
file.write(self._sep.join(config) + self._sep)


class Toml(BaseDumper):
Expand Down
94 changes: 59 additions & 35 deletions incipyt/_internal/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,23 @@ def __init__(self, data):
"""
self.data = data

def __getitem__(self, key):
if isinstance(self.data[key], abc.MutableMapping):
return TemplateDict(self.data[key])
elif isinstance(self.data[key], abc.MutableSequence):
return TemplateList(self.data[key])
def __getitem__(self, keys):
if not utils.is_nonstring_sequence(keys):
keys = (keys,)

config = self.data

for key in keys:
if key not in config:
raise KeyError(f"Index [{', '.join(keys)}] does not exist.")
config = config[key]

if isinstance(config, abc.MutableMapping):
return TemplateDict(config)
elif utils.is_nonstring_sequence(config):
return TemplateList(config)
else:
return self.data[key]
return config

def __iter__(self):
return iter(self.data)
Expand All @@ -342,41 +352,49 @@ def __delitem__(self, key):
)

def __setitem__(self, keys, value):
value, transform = Transform._get_transform(value)
if not utils.is_nonstring_sequence(keys):
keys = (keys,)
if utils.is_nonstring_sequence(keys):
config = self.data

if isinstance(value, abc.Mapping):
for k, v in value.items():
self[keys + (k,)] = Transform._get_transform(v, transform)
return
for key in keys[:-1]:
if key not in config:
config[key] = {}
config = config[key]

config = self.data
self[keys[:-1]][keys[-1]] = value
return

for key in keys[:-1]:
if key not in config:
config[key] = {}
config = config[key]
value, transform = Transform._get_transform(value)

key = keys[-1]
if isinstance(value, abc.Mapping):
if keys not in self.data:
self.data[keys] = {}

assert not utils.is_nonstring_sequence(
self.data[keys]
), f"{self.data[keys]} is already a sequence, cannot set to a dict."
for key, value in value.items():
TemplateDict(self.data[keys])[key] = Transform._get_transform(
value, transform
)

if utils.is_nonstring_sequence(value):
if key not in config:
config[key] = []
elif utils.is_nonstring_sequence(value):
if keys not in self.data:
self.data[keys] = []

assert not isinstance(
config[key], abc.Mapping
), f"{config[key]} is already a mapping, cannot set to a sequence."

config = TemplateList(config[key])
config += Transform._get_transform(value, transform)
self.data[keys], abc.Mapping
), f"{self.data[keys]} is already a mapping, cannot set to a list."
TemplateList(self.data[keys]).extend(
Transform._get_transform(value, transform)
)

else:
value = Transform._get_transform(value, transform)
if key in config:
config[key] = MultiStringTemplate(value, config[key])
if keys in self.data:
self.data[keys] = MultiStringTemplate(
Transform._get_transform(value, transform), self.data[keys]
)
else:
config[key] = Transform._get_value(value, transform)
self.data[keys] = Transform._get_value(value, transform)

def update(self, other=(), /, **kwds):
other, transform_other = Transform._get_transform(other)
Expand Down Expand Up @@ -417,7 +435,7 @@ def __init__(self, data):
self.data = data

def __getitem__(self, index):
if isinstance(self.data[index], abc.MutableSequence):
if utils.is_nonstring_sequence(self.data[index]):
return TemplateList(self.data[index])
elif isinstance(self.data[index], abc.MutableMapping):
return TemplateDict(self.data[index])
Expand Down Expand Up @@ -446,10 +464,16 @@ def __delitem__(self, value):
def insert(self, index, value):
value, transform = Transform._get_transform(value)

if isinstance(value, abc.Mapping):
if utils.is_nonstring_sequence(value):
self.data.insert(index, [])
TemplateList(self.data[index]).extend(
Transform._get_transform(value, transform)
)
elif isinstance(value, abc.Mapping):
self.data.insert(index, {})
dict_proxy = TemplateDict(self.data[index])
dict_proxy |= Transform._get_transform(value, transform)
TemplateDict(self.data[index]).update(
Transform._get_transform(value, transform)
)
else:
new_value = Transform._get_value(value, transform)
if new_value not in self.data:
Expand Down
77 changes: 52 additions & 25 deletions incipyt/project.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
"""TO-DO."""

import collections
import collections.abc
import logging
import os
import sys
from collections import abc

import click

from incipyt._internal import templates

from incipyt._internal.utils import EnvValue, formattable
from incipyt._internal.utils import EnvValue, formattable, is_nonstring_sequence

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -118,22 +118,44 @@ def clear(self):
def __init__(self):
self.clear()

def get_configuration(self, config_root):
def get_config_dict(self, config_root):
"""Get a configuration dictionary associated to the relative path `config_root`.
:param config_root: Relative path of the configuration file.
:type config_root: :class:`pathlib.Path`
:return: A reference to the configuration dictionary
:rtype: :class:`dict`
:rtype: :class:`incipyt._internal.templates.TempateDict`
"""
if config_root not in self._configurations:
logger.debug(
"Register configuration %s in project structure.", str(config_root)
)
self._configurations[config_root] = {}

assert isinstance(
self._configurations[config_root], abc.MutableMapping
), f"{config_root} is not a dict."
return templates.TemplateDict(self._configurations[config_root])

def get_config_list(self, config_root):
"""Get a configuration list associated to the relative path `config_root`.
:param config_root: Relative path of the configuration file.
:type config_root: :class:`pathlib.Path`
:return: A reference to the configuration list
:rtype: :class:`incipyt._internal.templates.TempateList`
"""
if config_root not in self._configurations:
logger.debug(
"Register configuration %s in project structure.", str(config_root)
)
self._configurations[config_root] = []

assert is_nonstring_sequence(
self._configurations[config_root]
), f"{config_root} is not a list."
return templates.TemplateList(self._configurations[config_root])

def commit(self):
"""Commit current project structure on disk.
Expand Down Expand Up @@ -169,32 +191,37 @@ def _visit(template):
replaced by their results. All nested structures will be recursively
visited and processed too.
:param template: The template dictionary to visit.
:type template: :class:`collections.abc.Mapping`
:param template: The template dictionary or list to visit.
:type template: :class:`abc.MutableMapping` of :class:`abc.MutableSequence`
"""
for key, value in template.items():
logger.debug("Visit %s to process environ variables.", key)

if formattable(value):
template[key] = value.format()

elif isinstance(value, collections.abc.MutableMapping):
_Structure._visit(value)
if is_nonstring_sequence(template):
for index, value in enumerate(template):
if formattable(value):
template[index] = value.format()
else:
_Structure._visit(value)
if not template[index]:
template[index] = None

while None in template:
template.remove(None)

elif isinstance(template, abc.MutableMapping): # noqa: SIM106
for key, value in template.items():
logger.debug("Visit %s to process environ variables.", key)

if formattable(value):
template[key] = value.format()
else:
_Structure._visit(value)
if not template[key]:
template[key] = None

elif isinstance(value, collections.abc.MutableSequence):
for index, element in enumerate(value):
if formattable(element):
template[key][index] = element.format()
elif isinstance(element, collections.abc.MutableMapping):
_Structure._visit(element)
template[key] = [element for element in template[key] if element]
if not template[key]:
template[key] = None
for key in [key for key, value in template.items() if value is None]:
del template[key]

for key in [key for key, value in template.items() if value is None]:
del template[key]
else:
raise TypeError(f"{type(template)} do not support visitation.")


structure = _Structure()
5 changes: 2 additions & 3 deletions incipyt/tools/git.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from incipyt import commands, project, signals, tools
from incipyt._internal import templates
from incipyt._internal.dumpers import Requirement
from incipyt._internal.dumpers import TextFile


class Git(tools.Tool):
Expand Down Expand Up @@ -29,8 +29,7 @@ def add_to_structure(self):
)

def _slot(self, pattern, **kwargs):
gitignore = project.structure.get_configuration(Requirement(".gitignore"))
gitignore[None] = [pattern]
project.structure.get_config_list(TextFile(".gitignore")).append(pattern)

def pre(self, workon):
"""Run `git init`.
Expand Down
59 changes: 31 additions & 28 deletions incipyt/tools/setuptools.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ def add_to_structure(self):
:raises RuntimeError: If a build-system is already setup im pyproject.toml.
"""
pyproject = project.structure.get_configuration(Toml("pyproject.toml"))
setup = project.structure.get_configuration(CfgIni("setup.cfg"))
pyproject = project.structure.get_config_dict(Toml("pyproject.toml"))
setup = project.structure.get_config_dict(CfgIni("setup.cfg"))

if "build-system" in pyproject:
raise RuntimeError("Build system already registered.")
Expand Down Expand Up @@ -108,19 +108,18 @@ def add_to_structure(self):
"{PACKAGE_DATA}/*", confirmed=True, PACKAGE_DATA="data"
)

project.structure.get_configuration(TextFile("LICENSE"))[
None
] = "Copyright (c) {AUTHOR_NAME} <{AUTHOR_EMAIL}>\n\n"
project.structure.get_config_list(TextFile("LICENSE", sep="\n\n")).append(
"Copyright (c) {AUTHOR_NAME} <{AUTHOR_EMAIL}>"
)

project.structure.get_configuration(
project.structure.get_config_list(
TextFile("{PROJECT_NAME}/__init__.py", sanitizer=sanitizers.package)
)[None] = "\n"
).append("")

project.structure.get_configuration(TextFile("README.md"))[
None
] = templates.StringTemplate(
textwrap.dedent(
"""\
project.structure.get_config_list(TextFile("README.md", sep="\n\n")).append(
templates.StringTemplate(
textwrap.dedent(
"""\
# {PROJECT_NAME}
{SUMMARY_DESCRIPTION}
Expand All @@ -129,20 +128,19 @@ def add_to_structure(self):
## Contribute
Copyright (c) {AUTHOR_NAME} <{AUTHOR_EMAIL}>\n
"""
),
value_error=False,
Copyright (c) {AUTHOR_NAME} <{AUTHOR_EMAIL}>"""
),
value_error=False,
)
)

project.structure.get_configuration(TextFile("setup.py"))[
None
] = textwrap.dedent(
"""\
project.structure.get_config_list(TextFile("setup.py")).append(
textwrap.dedent(
"""\
import setuptools
setuptools.setup()\n
"""
setuptools.setup()"""
)
)

signals.build_dependency.emit(dep_name=templates.Transform("build"))
Expand All @@ -158,16 +156,21 @@ def add_to_structure(self):
signals.vcs_ignore.emit(pattern=templates.Transform("*.egg-info"))

def _slot_classifier(self, classifier, **kwargs):
setup = project.structure.get_configuration(CfgIni("setup.cfg"))
setup["metadata", "classifiers"] = [classifier]
setup = project.structure.get_config_dict(CfgIni("setup.cfg"))
if ("metadata", "classifiers") not in setup:
setup["metadata", "classifiers"] = []
setup["metadata", "classifiers"].append(classifier)

def _slot_dependency(self, dep_name, **kwargs):
setup = project.structure.get_configuration(CfgIni("setup.cfg"))
setup["options.extras_require", "dev"] = [dep_name]
setup = project.structure.get_config_dict(CfgIni("setup.cfg"))
if ("options.extras_require", "dev") not in setup:
setup["options.extras_require", "dev"] = []
setup["options.extras_require", "dev"].append(dep_name)

def _slot_url(self, url_kind, url_value, **kwargs):
setup = project.structure.get_configuration(CfgIni("setup.cfg"))
setup["metadata", "project_urls", url_kind] = url_value
project.structure.get_config_dict(CfgIni("setup.cfg"))[
"metadata", "project_urls"
] = {url_kind: url_value}

def post(self, workon):
"""Editable install and build for test.
Expand Down
Loading

0 comments on commit 0a80f69

Please sign in to comment.