Skip to content

Commit 12b917e

Browse files
authored
Add support for .gitignore (#32)
* Add GitIgnore * Rename *Mixin -> *MixIn * Add {Project,Dir}.subpaths() to get an iterable of string subpaths * Add {Dir,File}.parent to get the {Project,Dir} the object is contained within * Update checklist and example in README * Fix windows style separators (\) in GitIgnore * Bump version to 1.6.0
1 parent 7c2993c commit 12b917e

File tree

11 files changed

+226
-118
lines changed

11 files changed

+226
-118
lines changed

.github/workflows/publish.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ jobs:
6161
run: make dist
6262
- name: check tag matches version
6363
run: ls dist/synth-a-py-${{ steps.tag.outputs.tag }}.tar.gz
64-
dist/synth-a-py-1.5.0.tar.gz dist/synth_a_py-${{ steps.tag.outputs.tag
65-
}}-py3-none-any.whl dist/synth_a_py-1.5.0-py3-none-any.whl
64+
dist/synth-a-py-1.6.0.tar.gz dist/synth_a_py-${{ steps.tag.outputs.tag
65+
}}-py3-none-any.whl dist/synth_a_py-1.6.0-py3-none-any.whl
6666
- name: create release
6767
id: create_release
6868
uses: actions/create-release@v1
@@ -79,8 +79,8 @@ jobs:
7979
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8080
with:
8181
upload_url: ${{ steps.create_release.outputs.upload_url }}
82-
asset_path: dist/synth-a-py-1.5.0.tar.gz
83-
asset_name: synth-a-py-1.5.0.tar.gz
82+
asset_path: dist/synth-a-py-1.6.0.tar.gz
83+
asset_name: synth-a-py-1.6.0.tar.gz
8484
asset_content_type: application/gzip
8585
- name: publish to pypi
8686
env:

.projenrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ class PoetryProject extends Project {
532532

533533
const project = new PoetryProject({
534534
name: "synth-a-py",
535-
version: "1.5.0",
535+
version: "1.6.0",
536536
description: "Project configuration as code",
537537
authors: ["Joseph Egan <joseph.s.egan@gmail.com>"],
538538
repository: "https://github.com/eganjs/synth-a-py",

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ publish: dist
2222
poetry publish
2323

2424
.PHONY: dist
25-
dist: dist/synth-a-py-1.5.0.tar.gz dist/synth_a_py-1.5.0-py3-none-any.whl
25+
dist: dist/synth-a-py-1.6.0.tar.gz dist/synth_a_py-1.6.0-py3-none-any.whl
2626

27-
dist/synth-a-py-1.5.0.tar.gz dist/synth_a_py-1.5.0-py3-none-any.whl: $(shell find synth_a_py -type f -name '*.py')
27+
dist/synth-a-py-1.6.0.tar.gz dist/synth_a_py-1.6.0-py3-none-any.whl: $(shell find synth_a_py -type f -name '*.py')
2828
poetry build
2929

3030
.venv: poetry.lock

README.md

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Project configuration as code
1414
- [ ] GitHub Action workflow?
1515
- [x] INI (for flake8/mypy config)
1616
- [ ] Makefile
17-
- [ ] .gitignore
17+
- [x] .gitignore
1818
- Add ./synth.py
1919
- Templates:
2020
- [ ] Poetry
@@ -72,20 +72,17 @@ with spec:
7272

7373
License.MIT("2020", ", ".join(authors))
7474

75-
SimpleFile(
76-
".gitignore",
77-
dedent(
78-
"""\
79-
*.egg
80-
*.egg-info/
81-
*.pyc
82-
.cache/
83-
.idea/
84-
.mypy_cache/
85-
.venv/
86-
dist/
87-
"""
88-
),
75+
GitIgnore(
76+
ignore=[
77+
"*.egg",
78+
"*.egg-info/",
79+
"*.pyc",
80+
".cache/",
81+
".idea/",
82+
".mypy_cache/",
83+
".venv/",
84+
"dist/",
85+
],
8986
)
9087

9188
SimpleFile(

poetry.lock

Lines changed: 81 additions & 79 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
44

55
[tool.poetry]
66
name = "synth-a-py"
7-
version = "1.5.0"
7+
version = "1.6.0"
88
description = "Project configuration as code"
99
authors = [ "Joseph Egan <joseph.s.egan@gmail.com>" ]
1010
license = "MIT"

synth_a_py/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
__version__ = "1.5.0"
1+
__version__ = "1.6.0"
22

33
from .base import Dir, File, Project
44
from .file import EmptyFile, SimpleFile
5+
from .gitignore import GitIgnore
56
from .ini import IniFile
67
from .license import License
78
from .toml import TomlFile
@@ -11,6 +12,7 @@
1112
"Dir",
1213
"EmptyFile",
1314
"File",
15+
"GitIgnore",
1416
"IniFile",
1517
"License",
1618
"Project",

synth_a_py/base.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from returns.functions import compose
88

9+
from .utils import init_mix_ins
10+
911
__all__ = [
1012
"File",
1113
"Project",
@@ -16,7 +18,7 @@
1618
PathResolver = Callable[[Path], Path]
1719

1820

19-
class _FileContainerMixin:
21+
class _FileContainerMixIn:
2022
def __init__(self) -> None:
2123
self.__store: Dict[str, Union[File, Dir]] = dict()
2224

@@ -37,30 +39,33 @@ def path_resolver(path: Path) -> Path:
3739
for subpath_resolver, subitem in item.walk():
3840
yield compose(path_resolver, subpath_resolver), subitem
3941

42+
def subpaths(self) -> Iterator[str]:
43+
return (str(path_resolver(Path("."))) for path_resolver, _ in self.walk())
44+
4045

4146
if TYPE_CHECKING:
42-
_ContextToken = Token[Optional[_FileContainerMixin]]
47+
_ContextToken = Token[Optional[_FileContainerMixIn]]
4348
else:
4449
_ContextToken = Token
4550

46-
__context: ContextVar[Optional[_FileContainerMixin]] = ContextVar(
51+
__context: ContextVar[Optional[_FileContainerMixIn]] = ContextVar(
4752
"__context", default=None
4853
)
4954

5055

51-
def _context_get() -> _FileContainerMixin:
56+
def _context_get() -> _FileContainerMixIn:
5257
project = __context.get()
5358
assert project is not None
5459
return project
5560

5661

57-
def _context_set_root(value: _FileContainerMixin) -> _ContextToken:
62+
def _context_set_root(value: _FileContainerMixIn) -> _ContextToken:
5863
project = __context.get()
5964
assert project is None
6065
return __context.set(value)
6166

6267

63-
def _context_set(value: _FileContainerMixin) -> _ContextToken:
68+
def _context_set(value: _FileContainerMixIn) -> _ContextToken:
6469
project = __context.get()
6570
assert project is not None
6671
return __context.set(value)
@@ -72,20 +77,27 @@ def _context_reset(token: _ContextToken) -> None:
7277
__context.reset(token)
7378

7479

75-
class File:
76-
def __init__(self, name: str):
80+
class _ChildMixIn:
81+
def __init__(self) -> None:
82+
self.parent = _context_get()
83+
assert isinstance(self, File) or isinstance(self, Dir)
84+
self.parent.add(self)
85+
86+
87+
class File(_ChildMixIn):
88+
def __init__(self, name: str) -> None:
7789
self.name = name
78-
_context_get().add(self)
90+
init_mix_ins(self, File)
7991

8092
@abstractmethod
8193
def synth_content(self) -> str:
8294
...
8395

8496

85-
class _ContextMixin(_FileContainerMixin):
97+
class _ContextMixIn(_FileContainerMixIn):
8698
def __init__(self) -> None:
87-
super().__init__()
8899
self.__context_token: Optional[_ContextToken] = None
100+
init_mix_ins(self, _ContextMixIn)
89101

90102
def __enter__(self) -> None:
91103
assert self.__context_token is None
@@ -105,7 +117,7 @@ def __exit__(
105117
self.__context_token = None
106118

107119

108-
class Project(_ContextMixin):
120+
class Project(_ContextMixIn):
109121
def synth(self, root: Optional[Path] = None) -> None:
110122
if root is None:
111123
root = Path.cwd()
@@ -126,8 +138,7 @@ def synth(self, root: Optional[Path] = None) -> None:
126138
path.chmod(0o444)
127139

128140

129-
class Dir(_ContextMixin):
141+
class Dir(_ContextMixIn, _ChildMixIn):
130142
def __init__(self, name: str) -> None:
131-
super().__init__()
132143
self.name = name
133-
_context_get().add(self)
144+
init_mix_ins(self, Dir)

synth_a_py/gitignore.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from pathlib import Path
2+
from typing import List, Optional
3+
4+
from .base import File
5+
from .utils import ensure_nl
6+
7+
__all__ = ["GitIgnore"]
8+
9+
10+
class GitIgnore(File):
11+
def __init__(
12+
self,
13+
ignore: Optional[List[str]] = None,
14+
allow: Optional[List[str]] = None,
15+
):
16+
super().__init__(".gitignore")
17+
self.ignore = ignore or []
18+
self.allow = allow or []
19+
20+
def synth_content(self) -> str:
21+
return ensure_nl(
22+
"\n".join(
23+
[
24+
*self.ignore,
25+
*(
26+
f"!{'/'.join(Path(path).parts)}"
27+
for path in self.parent.subpaths()
28+
),
29+
*(f"!{path}" for path in self.allow),
30+
]
31+
)
32+
)

synth_a_py/utils.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
11
__all__ = [
22
"ensure_nl",
3+
"init_mix_ins",
34
]
45

6+
from inspect import Parameter, Signature, signature
7+
from typing import Any
8+
9+
from typing_extensions import Final
10+
511

612
def ensure_nl(s: str) -> str:
713
return s.rstrip() + "\n"
14+
15+
16+
no_param_init: Final[Signature] = Signature(
17+
(Parameter("self", Parameter.POSITIONAL_OR_KEYWORD),),
18+
return_annotation=None,
19+
)
20+
21+
22+
def init_mix_ins(self: Any, t: type) -> None:
23+
bases = t.__bases__
24+
for base in bases:
25+
init = getattr(base, "__init__")
26+
if signature(init) == no_param_init:
27+
init(self)

0 commit comments

Comments
 (0)