frigobar 13__tar.gz → 14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {frigobar-13 → frigobar-14}/PKG-INFO +1 -1
- {frigobar-13 → frigobar-14}/frigobar/frigobar.py +82 -0
- {frigobar-13 → frigobar-14}/pyproject.toml +1 -1
- {frigobar-13 → frigobar-14}/tests/test_frigobar.py +185 -0
- {frigobar-13 → frigobar-14}/uv.lock +1 -1
- {frigobar-13 → frigobar-14}/.github/workflows/publish-pypi.yml +0 -0
- {frigobar-13 → frigobar-14}/.gitignore +0 -0
- {frigobar-13 → frigobar-14}/frigobar/__init__.py +0 -0
- {frigobar-13 → frigobar-14}/frigobar/__main__.py +0 -0
- {frigobar-13 → frigobar-14}/frigobar/cli.py +0 -0
- {frigobar-13 → frigobar-14}/license +0 -0
- {frigobar-13 → frigobar-14}/readme.md +0 -0
- {frigobar-13 → frigobar-14}/tests/__init__.py +0 -0
- {frigobar-13 → frigobar-14}/tests/conftest.py +0 -0
- {frigobar-13 → frigobar-14}/tests/script_folder/.gitignore +0 -0
- {frigobar-13 → frigobar-14}/tests/script_folder/another_script.py +0 -0
- {frigobar-13 → frigobar-14}/tests/script_folder/pyproject.toml +0 -0
- {frigobar-13 → frigobar-14}/tests/script_folder/requirements.txt +0 -0
- {frigobar-13 → frigobar-14}/tests/script_folder/script.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frigobar
|
|
3
|
-
Version:
|
|
3
|
+
Version: 14
|
|
4
4
|
Summary: Distribute Python scripts to Windows machines without freezing them.
|
|
5
5
|
Project-URL: Homepage, https://github.com/ubalklen/Frigobar
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/ubalklen/Frigobar/issues
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import glob
|
|
2
2
|
import os
|
|
3
3
|
import shutil
|
|
4
|
+
import tomllib
|
|
4
5
|
from subprocess import CalledProcessError, Popen, run
|
|
5
6
|
|
|
6
7
|
import pathspec
|
|
@@ -126,6 +127,72 @@ def _create_ignore_fn(base_dir, target_directory, git_files=None, gitignore_spec
|
|
|
126
127
|
return ignore_fn
|
|
127
128
|
|
|
128
129
|
|
|
130
|
+
PYPROJECT_PATH_FIELDS = [
|
|
131
|
+
["tool", "setuptools", "packages", "find", "where"],
|
|
132
|
+
["tool", "pytest", "ini_options", "pythonpath"],
|
|
133
|
+
["tool", "pytest", "ini_options", "testpaths"],
|
|
134
|
+
["tool", "hatch", "build", "targets", "wheel", "packages"],
|
|
135
|
+
["tool", "hatch", "build", "targets", "sdist", "include"],
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _get_nested(data, keys):
|
|
140
|
+
for key in keys:
|
|
141
|
+
if not isinstance(data, dict) or key not in data:
|
|
142
|
+
return None
|
|
143
|
+
data = data[key]
|
|
144
|
+
return data
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _compute_path_replacement(value, include_dir_name, pyproject_inside_include):
|
|
148
|
+
if pyproject_inside_include:
|
|
149
|
+
if value == ".":
|
|
150
|
+
return f"script/{include_dir_name}"
|
|
151
|
+
return f"script/{include_dir_name}/{value}"
|
|
152
|
+
else:
|
|
153
|
+
if value == include_dir_name:
|
|
154
|
+
return f"script/{include_dir_name}"
|
|
155
|
+
if value.startswith(include_dir_name + "/"):
|
|
156
|
+
return f"script/{value}"
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _patch_pyproject_paths(pyproject_path, include_dir_name, pyproject_inside_include):
|
|
161
|
+
with open(pyproject_path, "rb") as f:
|
|
162
|
+
data = tomllib.load(f)
|
|
163
|
+
|
|
164
|
+
replacements = []
|
|
165
|
+
for field_path in PYPROJECT_PATH_FIELDS:
|
|
166
|
+
value = _get_nested(data, field_path)
|
|
167
|
+
if value is None:
|
|
168
|
+
continue
|
|
169
|
+
if isinstance(value, list):
|
|
170
|
+
for item in value:
|
|
171
|
+
if isinstance(item, str):
|
|
172
|
+
new_val = _compute_path_replacement(
|
|
173
|
+
item, include_dir_name, pyproject_inside_include
|
|
174
|
+
)
|
|
175
|
+
if new_val:
|
|
176
|
+
replacements.append((item, new_val))
|
|
177
|
+
elif isinstance(value, str):
|
|
178
|
+
new_val = _compute_path_replacement(value, include_dir_name, pyproject_inside_include)
|
|
179
|
+
if new_val:
|
|
180
|
+
replacements.append((value, new_val))
|
|
181
|
+
|
|
182
|
+
if not replacements:
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
with open(pyproject_path, "r", encoding="utf-8") as f:
|
|
186
|
+
content = f.read()
|
|
187
|
+
|
|
188
|
+
for old_val, new_val in replacements:
|
|
189
|
+
content = content.replace(f'"{old_val}"', f'"{new_val}"')
|
|
190
|
+
content = content.replace(f"'{old_val}'", f"'{new_val}'")
|
|
191
|
+
|
|
192
|
+
with open(pyproject_path, "w", encoding="utf-8") as f:
|
|
193
|
+
f.write(content)
|
|
194
|
+
|
|
195
|
+
|
|
129
196
|
def create_frigobar(
|
|
130
197
|
script_path: str,
|
|
131
198
|
target_directory: str = "frigobar",
|
|
@@ -219,6 +286,21 @@ def create_frigobar(
|
|
|
219
286
|
if os.path.exists(pyproject_path):
|
|
220
287
|
shutil.copy(pyproject_path, target_directory)
|
|
221
288
|
|
|
289
|
+
# Patch pyproject.toml paths when using include_directory
|
|
290
|
+
if include_directory:
|
|
291
|
+
target_pyproject = os.path.join(target_directory, "pyproject.toml")
|
|
292
|
+
if os.path.exists(target_pyproject):
|
|
293
|
+
include_dir_name = os.path.basename(include_directory)
|
|
294
|
+
if pyproject_file:
|
|
295
|
+
pyproject_source_dir = os.path.dirname(os.path.abspath(pyproject_file))
|
|
296
|
+
else:
|
|
297
|
+
pyproject_source_dir = os.path.dirname(script_path)
|
|
298
|
+
include_abs = os.path.abspath(include_directory)
|
|
299
|
+
pyproject_inside_include = os.path.commonpath(
|
|
300
|
+
[pyproject_source_dir, include_abs]
|
|
301
|
+
) == os.path.normpath(include_abs)
|
|
302
|
+
_patch_pyproject_paths(target_pyproject, include_dir_name, pyproject_inside_include)
|
|
303
|
+
|
|
222
304
|
# Create bat file
|
|
223
305
|
if include_directory:
|
|
224
306
|
include_dir_name = os.path.basename(include_directory)
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "frigobar"
|
|
7
|
-
version = "
|
|
7
|
+
version = "14"
|
|
8
8
|
description = "Distribute Python scripts to Windows machines without freezing them."
|
|
9
9
|
authors = [
|
|
10
10
|
{name="ubalklen", email="42127323+ubalklen@users.noreply.github.com"},
|
|
@@ -613,3 +613,188 @@ def test_create_frigobar_copy_directory_with_non_ascii_filenames(tmp_path):
|
|
|
613
613
|
assert path.exists(path.join(str(target), "script", "script.py"))
|
|
614
614
|
assert path.exists(path.join(str(target), "script", "Notificação.docx"))
|
|
615
615
|
assert path.exists(path.join(str(target), "script", "résumé.txt"))
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def test_patch_pyproject_paths_setuptools_where_outside(tmp_path):
|
|
619
|
+
"""When pyproject.toml is outside include_directory, paths matching include_dir_name are prefixed"""
|
|
620
|
+
source_dir = tmp_path / "project"
|
|
621
|
+
source_dir.mkdir()
|
|
622
|
+
src_dir = source_dir / "src"
|
|
623
|
+
src_dir.mkdir()
|
|
624
|
+
(src_dir / "main.py").write_text("print('hello')")
|
|
625
|
+
(src_dir / "__init__.py").write_text("")
|
|
626
|
+
|
|
627
|
+
pyproject_content = '[project]\nname = "my-pkg"\nversion = "1.0"\n\n[tool.setuptools.packages.find]\nwhere = ["src"]\n'
|
|
628
|
+
pyproject_file = source_dir / "pyproject.toml"
|
|
629
|
+
pyproject_file.write_text(pyproject_content)
|
|
630
|
+
|
|
631
|
+
target = tmp_path / "dist"
|
|
632
|
+
|
|
633
|
+
with patch("frigobar.frigobar._get_git_non_ignored_files", return_value=None):
|
|
634
|
+
frigobar.create_frigobar(
|
|
635
|
+
script_path=str(src_dir / "main.py"),
|
|
636
|
+
target_directory=str(target),
|
|
637
|
+
include_directory=str(src_dir),
|
|
638
|
+
pyproject_file=str(pyproject_file),
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
with open(path.join(str(target), "pyproject.toml"), "r") as f:
|
|
642
|
+
content = f.read()
|
|
643
|
+
assert 'where = ["script/src"]' in content
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def test_patch_pyproject_paths_setuptools_where_inside(tmp_path):
|
|
647
|
+
"""When pyproject.toml is inside include_directory, all paths get script/<include_dir_name> prefix"""
|
|
648
|
+
source_dir = tmp_path / "project"
|
|
649
|
+
source_dir.mkdir()
|
|
650
|
+
lib_dir = source_dir / "lib"
|
|
651
|
+
lib_dir.mkdir()
|
|
652
|
+
(lib_dir / "__init__.py").write_text("")
|
|
653
|
+
(source_dir / "main.py").write_text("print('hello')")
|
|
654
|
+
|
|
655
|
+
pyproject_content = '[project]\nname = "my-pkg"\nversion = "1.0"\n\n[tool.setuptools.packages.find]\nwhere = ["lib"]\n'
|
|
656
|
+
pyproject_file = source_dir / "pyproject.toml"
|
|
657
|
+
pyproject_file.write_text(pyproject_content)
|
|
658
|
+
|
|
659
|
+
target = tmp_path / "dist"
|
|
660
|
+
|
|
661
|
+
with patch("frigobar.frigobar._get_git_non_ignored_files", return_value=None):
|
|
662
|
+
frigobar.create_frigobar(
|
|
663
|
+
script_path=str(source_dir / "main.py"),
|
|
664
|
+
target_directory=str(target),
|
|
665
|
+
include_directory=str(source_dir),
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
with open(path.join(str(target), "pyproject.toml"), "r") as f:
|
|
669
|
+
content = f.read()
|
|
670
|
+
assert 'where = ["script/project/lib"]' in content
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
def test_patch_pyproject_paths_dot_where_inside(tmp_path):
|
|
674
|
+
"""When pyproject.toml is inside include_directory and where=['.'], it becomes script/<dir>"""
|
|
675
|
+
source_dir = tmp_path / "myproject"
|
|
676
|
+
source_dir.mkdir()
|
|
677
|
+
(source_dir / "main.py").write_text("print('hello')")
|
|
678
|
+
(source_dir / "__init__.py").write_text("")
|
|
679
|
+
|
|
680
|
+
pyproject_content = '[project]\nname = "my-pkg"\nversion = "1.0"\n\n[tool.setuptools.packages.find]\nwhere = ["."]\n'
|
|
681
|
+
pyproject_file = source_dir / "pyproject.toml"
|
|
682
|
+
pyproject_file.write_text(pyproject_content)
|
|
683
|
+
|
|
684
|
+
target = tmp_path / "dist"
|
|
685
|
+
|
|
686
|
+
with patch("frigobar.frigobar._get_git_non_ignored_files", return_value=None):
|
|
687
|
+
frigobar.create_frigobar(
|
|
688
|
+
script_path=str(source_dir / "main.py"),
|
|
689
|
+
target_directory=str(target),
|
|
690
|
+
include_directory=str(source_dir),
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
with open(path.join(str(target), "pyproject.toml"), "r") as f:
|
|
694
|
+
content = f.read()
|
|
695
|
+
assert 'where = ["script/myproject"]' in content
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def test_patch_pyproject_paths_hatch_packages(tmp_path):
|
|
699
|
+
"""Hatch build targets packages field is also patched"""
|
|
700
|
+
source_dir = tmp_path / "project"
|
|
701
|
+
source_dir.mkdir()
|
|
702
|
+
src_dir = source_dir / "src"
|
|
703
|
+
src_dir.mkdir()
|
|
704
|
+
pkg_dir = src_dir / "mypkg"
|
|
705
|
+
pkg_dir.mkdir()
|
|
706
|
+
(pkg_dir / "__init__.py").write_text("")
|
|
707
|
+
(src_dir / "main.py").write_text("print('hello')")
|
|
708
|
+
|
|
709
|
+
pyproject_content = '[project]\nname = "my-pkg"\nversion = "1.0"\n\n[tool.hatch.build.targets.wheel]\npackages = ["src/mypkg"]\n'
|
|
710
|
+
pyproject_file = source_dir / "pyproject.toml"
|
|
711
|
+
pyproject_file.write_text(pyproject_content)
|
|
712
|
+
|
|
713
|
+
target = tmp_path / "dist"
|
|
714
|
+
|
|
715
|
+
with patch("frigobar.frigobar._get_git_non_ignored_files", return_value=None):
|
|
716
|
+
frigobar.create_frigobar(
|
|
717
|
+
script_path=str(src_dir / "main.py"),
|
|
718
|
+
target_directory=str(target),
|
|
719
|
+
include_directory=str(src_dir),
|
|
720
|
+
pyproject_file=str(pyproject_file),
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
with open(path.join(str(target), "pyproject.toml"), "r") as f:
|
|
724
|
+
content = f.read()
|
|
725
|
+
assert 'packages = ["script/src/mypkg"]' in content
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def test_patch_pyproject_paths_no_patch_without_include_directory(tmp_path):
|
|
729
|
+
"""pyproject.toml is not patched when include_directory is not used"""
|
|
730
|
+
source_dir = tmp_path / "project"
|
|
731
|
+
source_dir.mkdir()
|
|
732
|
+
(source_dir / "main.py").write_text("print('hello')")
|
|
733
|
+
|
|
734
|
+
pyproject_content = '[project]\nname = "my-pkg"\nversion = "1.0"\n\n[tool.setuptools.packages.find]\nwhere = ["src"]\n'
|
|
735
|
+
pyproject_file = source_dir / "pyproject.toml"
|
|
736
|
+
pyproject_file.write_text(pyproject_content)
|
|
737
|
+
|
|
738
|
+
target = tmp_path / "dist"
|
|
739
|
+
|
|
740
|
+
frigobar.create_frigobar(
|
|
741
|
+
script_path=str(source_dir / "main.py"),
|
|
742
|
+
target_directory=str(target),
|
|
743
|
+
pyproject_file=str(pyproject_file),
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
with open(path.join(str(target), "pyproject.toml"), "r") as f:
|
|
747
|
+
content = f.read()
|
|
748
|
+
assert 'where = ["src"]' in content
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def test_patch_pyproject_paths_no_patch_with_requirements(tmp_path):
|
|
752
|
+
"""pyproject.toml is not present when requirements_file is used"""
|
|
753
|
+
source_dir = tmp_path / "project"
|
|
754
|
+
source_dir.mkdir()
|
|
755
|
+
src_dir = source_dir / "src"
|
|
756
|
+
src_dir.mkdir()
|
|
757
|
+
(src_dir / "main.py").write_text("print('hello')")
|
|
758
|
+
|
|
759
|
+
req_file = source_dir / "requirements.txt"
|
|
760
|
+
req_file.write_text("requests\n")
|
|
761
|
+
|
|
762
|
+
target = tmp_path / "dist"
|
|
763
|
+
|
|
764
|
+
with patch("frigobar.frigobar._get_git_non_ignored_files", return_value=None):
|
|
765
|
+
frigobar.create_frigobar(
|
|
766
|
+
script_path=str(src_dir / "main.py"),
|
|
767
|
+
target_directory=str(target),
|
|
768
|
+
include_directory=str(src_dir),
|
|
769
|
+
requirements_file=str(req_file),
|
|
770
|
+
python_version="3.12",
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
assert not path.exists(path.join(str(target), "pyproject.toml"))
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
def test_patch_pyproject_paths_pytest_pythonpath(tmp_path):
|
|
777
|
+
"""pytest pythonpath field is also patched"""
|
|
778
|
+
source_dir = tmp_path / "project"
|
|
779
|
+
source_dir.mkdir()
|
|
780
|
+
src_dir = source_dir / "src"
|
|
781
|
+
src_dir.mkdir()
|
|
782
|
+
(src_dir / "main.py").write_text("print('hello')")
|
|
783
|
+
|
|
784
|
+
pyproject_content = '[project]\nname = "my-pkg"\nversion = "1.0"\n\n[tool.pytest.ini_options]\npythonpath = ["src"]\ntestpaths = ["tests"]\n'
|
|
785
|
+
pyproject_file = source_dir / "pyproject.toml"
|
|
786
|
+
pyproject_file.write_text(pyproject_content)
|
|
787
|
+
|
|
788
|
+
target = tmp_path / "dist"
|
|
789
|
+
|
|
790
|
+
with patch("frigobar.frigobar._get_git_non_ignored_files", return_value=None):
|
|
791
|
+
frigobar.create_frigobar(
|
|
792
|
+
script_path=str(src_dir / "main.py"),
|
|
793
|
+
target_directory=str(target),
|
|
794
|
+
include_directory=str(src_dir),
|
|
795
|
+
pyproject_file=str(pyproject_file),
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
with open(path.join(str(target), "pyproject.toml"), "r") as f:
|
|
799
|
+
content = f.read()
|
|
800
|
+
assert 'pythonpath = ["script/src"]' in content
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|