Skip to content

Commit 143a3cd

Browse files
committed
feat: support reading metadata from package.json
1 parent 9ed4fdd commit 143a3cd

File tree

4 files changed

+211
-86
lines changed

4 files changed

+211
-86
lines changed

hatch_nodejs_version/metadata_source.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def _parse_repository(self, repository: str | dict[str, str]) -> str:
9292
def update(self, metadata: dict[str, Any]):
9393
package = self.load_package_data()
9494

95+
metadata["name"] = package["name"]
96+
9597
if "author" in package:
9698
metadata["author"] = self._parse_person(package["author"])
9799

tests/conftest.py

Lines changed: 27 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
#
44
# SPDX-License-Identifier: MIT
55
import errno
6+
import os
67
import shutil
78
import stat
89
import tempfile
910
from contextlib import contextmanager
10-
import os
11+
import pathlib
12+
1113
import pytest
1214

1315

@@ -21,6 +23,27 @@ def create_file(path, contents):
2123
f.write(contents)
2224

2325

26+
@contextmanager
27+
def create_project(directory):
28+
project_dir = directory / "my-app"
29+
os.mkdir(project_dir)
30+
31+
package_dir = project_dir / "my_app"
32+
os.mkdir(package_dir)
33+
34+
touch_file(package_dir / "__init__.py")
35+
touch_file(package_dir / "foo.py")
36+
touch_file(package_dir / "bar.py")
37+
touch_file(package_dir / "baz.py")
38+
39+
origin = os.getcwd()
40+
os.chdir(project_dir)
41+
try:
42+
yield project_dir
43+
finally:
44+
os.chdir(origin)
45+
46+
2447
def handle_remove_readonly(func, path, exc): # no cov
2548
# PermissionError: [WinError 5] Access is denied: '...\\.git\\...'
2649
if func in (os.rmdir, os.remove, os.unlink) and exc[1].errno == errno.EACCES:
@@ -34,63 +57,13 @@ def handle_remove_readonly(func, path, exc): # no cov
3457
def temp_dir():
3558
directory = tempfile.mkdtemp()
3659
try:
37-
directory = os.path.realpath(directory)
60+
directory = pathlib.Path(os.path.realpath(directory))
3861
yield directory
3962
finally:
4063
shutil.rmtree(directory, ignore_errors=False, onerror=handle_remove_readonly)
4164

4265

43-
@contextmanager
44-
def create_project(directory, metadata, version):
45-
project_dir = os.path.join(directory, "my-app")
46-
os.mkdir(project_dir)
47-
48-
project_file = os.path.join(project_dir, "pyproject.toml")
49-
create_file(project_file, metadata)
50-
51-
package_dir = os.path.join(project_dir, "my_app")
52-
os.mkdir(package_dir)
53-
54-
package_file = os.path.join(project_dir, "package.json")
55-
package = f"""
56-
{{
57-
"name": "my-awesome-package",
58-
"version": "{version}",
59-
60-
}}
61-
"""
62-
create_file(package_file, package)
63-
64-
other_package_file = os.path.join(project_dir, "other-package.json")
65-
create_file(other_package_file, package)
66-
67-
touch_file(os.path.join(package_dir, "__init__.py"))
68-
touch_file(os.path.join(package_dir, "foo.py"))
69-
touch_file(os.path.join(package_dir, "bar.py"))
70-
touch_file(os.path.join(package_dir, "baz.py"))
71-
72-
origin = os.getcwd()
73-
os.chdir(project_dir)
74-
try:
75-
yield project_dir
76-
finally:
77-
os.chdir(origin)
78-
79-
8066
@pytest.fixture
81-
def new_project(temp_dir, request):
82-
with create_project(
83-
temp_dir,
84-
"""\
85-
[build-system]
86-
requires = ["hatchling", "hatch-vcs"]
87-
build-backend = "hatchling.build"
88-
[project]
89-
name = "my-app"
90-
dynamic = ["version"]
91-
[tool.hatch.version]
92-
source = "nodejs"
93-
""",
94-
request.param,
95-
) as project:
67+
def project(temp_dir):
68+
with create_project(temp_dir) as project:
9669
yield project

tests/test_metadata_config.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# SPDX-FileCopyrightText: 2022-present Angus Hollands <[email protected]>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
import pytest
5+
6+
from hatch_nodejs_version.metadata_source import NodeJSMetadataSource
7+
8+
9+
class TestMetadata:
10+
@pytest.mark.parametrize(
11+
"alt_package_json",
12+
[None, "package-other.json"],
13+
)
14+
@pytest.mark.parametrize(
15+
"pyproject_field",
16+
["urls", "description", "name", "keywords", "author", "maintainers", "license"],
17+
)
18+
def test_metadata_from_package(self, project, alt_package_json, pyproject_field):
19+
# Create a simple project
20+
(project / "pyproject.toml").write_text(
21+
f"""
22+
[build - system]
23+
requires = ["hatchling", "hatch-vcs"]
24+
build - backend = "hatchling.build"
25+
[project]
26+
name = "my-app"
27+
version = "0.0.0"
28+
[tool.hatch.metadata.hooks.nodejs]
29+
"""
30+
)
31+
package_json = "package.json" if alt_package_json is None else alt_package_json
32+
(project / package_json).write_text(
33+
"""
34+
{
35+
"name": "my-app",
36+
"version": "1.0.0",
37+
"description": "A terrible package",
38+
"keywords": [
39+
"what",
40+
"is",
41+
"the",
42+
"time"
43+
],
44+
"homepage": "https://where-the-heart-is.com",
45+
"bugs": {
46+
"url": "https://www.send-help.com",
47+
"email": "[email protected]"
48+
},
49+
"license": "MIT",
50+
"author": {
51+
"name": "Alice Roberts",
52+
"email": "[email protected]"
53+
},
54+
"contributors": [
55+
{
56+
"name": "Isaac Newton",
57+
"email": "[email protected]"
58+
}
59+
],
60+
"repository": {
61+
"type": "git",
62+
"url": "https://github.com/some/code.git"
63+
}
64+
}
65+
"""
66+
)
67+
config = {} if alt_package_json is None else {"path": alt_package_json}
68+
metadata_source = NodeJSMetadataSource(project, config=config)
69+
70+
metadata = {}
71+
metadata_source.update(metadata)
72+
73+
assert metadata["license"] == "MIT"
74+
assert metadata["urls"] == {
75+
"bug tracker": "https://www.send-help.com",
76+
"repository": "https://github.com/some/code.git",
77+
"homepage": "https://where-the-heart-is.com",
78+
}
79+
assert metadata["author"] == {
80+
"name": "Alice Roberts",
81+
"email": "[email protected]",
82+
}
83+
84+
assert metadata["maintainers"] == [
85+
{"name": "Isaac Newton", "email": "[email protected]"}
86+
]
87+
assert metadata["keywords"] == ["what", "is", "the", "time"]
88+
assert metadata["description"] == "A terrible package"
89+
assert metadata["name"] == "my-app"

tests/test_version_config.py

Lines changed: 93 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
#
33
# SPDX-License-Identifier: MIT
44
import pytest
5+
import json
56

67
from hatch_nodejs_version.version_source import NodeJSVersionSource
78

8-
99
GOOD_NODE_PYTHON_VERSIONS = [
1010
("1.4.5", "1.4.5"),
1111
("1.4.5-a0", "1.4.5a0"),
@@ -33,45 +33,106 @@
3333
]
3434

3535

36-
class TestDefault:
36+
class TestVersion:
3737
@pytest.mark.parametrize(
38-
"new_project, python_version",
38+
"node_version, python_version",
3939
GOOD_NODE_PYTHON_VERSIONS,
40-
indirect=["new_project"],
4140
)
42-
@pytest.mark.parametrize("config", [{"path": "other-package.json"}, {}])
43-
def test_read_correct(self, new_project, python_version, config):
44-
version_source = NodeJSVersionSource(new_project, config)
45-
data = version_source.get_version_data()
46-
assert data["version"] == python_version
41+
def test_parse_correct(self, node_version, python_version):
42+
node_version_parsed = NodeJSVersionSource.python_version_to_node(python_version)
43+
assert node_version_parsed == node_version
4744

48-
@pytest.mark.parametrize("python_version,node_version", GOOD_NODE_PYTHON_VERSIONS)
49-
@pytest.mark.parametrize("config", [{"path": "other-package.json"}, {}])
50-
@pytest.mark.parametrize("new_project", ["1.0.0"], indirect=True)
51-
def test_write_correct(self, new_project, python_version, node_version, config):
52-
version_source = NodeJSVersionSource(new_project, config)
53-
data = version_source.get_version_data()
54-
version_source.set_version(python_version, data)
55-
data = version_source.get_version_data()
56-
assert data["version"] == node_version
45+
@pytest.mark.parametrize(
46+
"python_version",
47+
BAD_PYTHON_VERSIONS,
48+
)
49+
def test_parse_python_incorrect(self, python_version):
50+
with pytest.raises(ValueError, match=".* did not match regex"):
51+
NodeJSVersionSource.python_version_to_node(python_version)
5752

5853
@pytest.mark.parametrize(
59-
"new_project",
54+
"node_version",
6055
BAD_NODE_VERSIONS,
61-
indirect=["new_project"],
6256
)
63-
@pytest.mark.parametrize("config", [{"path": "other-package.json"}, {}])
64-
def test_read_incorrect(self, new_project, config):
65-
version_source = NodeJSVersionSource(new_project, config)
66-
57+
def test_parse_python_incorrect(self, node_version):
6758
with pytest.raises(ValueError, match=".* did not match regex"):
68-
version_source.get_version_data()
59+
NodeJSVersionSource.node_version_to_python(node_version)
6960

70-
@pytest.mark.parametrize("python_version,", BAD_PYTHON_VERSIONS)
71-
@pytest.mark.parametrize("new_project", ["1.0.0"], indirect=True)
72-
@pytest.mark.parametrize("config", [{"path": "other-package.json"}, {}])
73-
def test_write_incorrect(self, new_project, python_version, config):
74-
version_source = NodeJSVersionSource(new_project, config)
61+
@pytest.mark.parametrize(
62+
"node_version, python_version",
63+
GOOD_NODE_PYTHON_VERSIONS,
64+
)
65+
@pytest.mark.parametrize(
66+
"alt_package_json",
67+
[None, "package-other.json"],
68+
)
69+
def test_version_from_package(
70+
self, project, node_version, python_version, alt_package_json
71+
):
72+
# Create a simple project
73+
(project / "pyproject.toml").write_text(
74+
"""
75+
[build - system]
76+
requires = ["hatchling", "hatch-vcs"]
77+
build - backend = "hatchling.build"
78+
[project]
79+
name = "my-app"
80+
dynamic = ["version"]
81+
[tool.hatch.version]
82+
source = "nodejs"
83+
"""
84+
)
85+
package_json = "package.json" if alt_package_json is None else alt_package_json
86+
(project / package_json).write_text(
87+
f"""
88+
{{
89+
"name": "my-app",
90+
"version": "{node_version}"
91+
}}
92+
"""
93+
)
94+
config = {} if alt_package_json is None else {"path": alt_package_json}
95+
version_source = NodeJSVersionSource(project, config=config)
7596
data = version_source.get_version_data()
76-
with pytest.raises(ValueError, match=".* did not match regex"):
77-
version_source.set_version(python_version, data)
97+
assert data['version'] == python_version
98+
99+
@pytest.mark.parametrize(
100+
"node_version, python_version",
101+
GOOD_NODE_PYTHON_VERSIONS,
102+
)
103+
@pytest.mark.parametrize(
104+
"alt_package_json",
105+
[None, "package-other.json"],
106+
)
107+
def test_version_to_package(
108+
self, project, node_version, python_version, alt_package_json
109+
):
110+
package_json = "package.json" if alt_package_json is None else alt_package_json
111+
(project / "pyproject.toml").write_text(
112+
"""
113+
[build - system]
114+
requires = ["hatchling", "hatch-vcs"]
115+
build - backend = "hatchling.build"
116+
[project]
117+
name = "my-app"
118+
dynamic = ["version"]
119+
[tool.hatch.version]
120+
source = "nodejs"
121+
"""
122+
)
123+
(project / package_json).write_text(
124+
f"""
125+
{{
126+
"name": "my-app",
127+
"version": "0.0.0"
128+
}}
129+
"""
130+
)
131+
config = {} if alt_package_json is None else {"path": alt_package_json}
132+
version_source = NodeJSVersionSource(project, config=config)
133+
version_data = version_source.get_version_data()
134+
version_source.set_version(python_version, version_data)
135+
136+
written_package = json.loads((project / package_json).read_text())
137+
assert written_package['version'] == node_version
138+

0 commit comments

Comments
 (0)