Skip to content

Commit

Permalink
Merge pull request #56 from facundobatista/packaging-for-importer
Browse files Browse the repository at this point in the history
Provide the 'packaging' module for unpacker
  • Loading branch information
facundobatista authored Apr 20, 2023
2 parents 48f54a5 + 31827d5 commit 293d432
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 20 deletions.
49 changes: 43 additions & 6 deletions PRESS_RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ With PyEmpaq you can convert any Python project into a single `.pyz` file with a

That single file is everything that needs to be distributed. When the final user executes it, the original project will be expanded, its dependencies installed in a virtualenv, and then executed. Note that no special permissions or privileges are required, as everything happens in the user environment.

Both the packaging and the execution are fully multiplatorm. This means that you can pack a project in Linux, Windows, Mac or whatever, and it will run ok in Linux, Windows, Mac or whatever. The only requirement is Python to be already installed.
Both the packaging and the execution are fully multiplatorm. This means that you can pack a project in Linux, Windows, MacOS or whatever, and it will run ok in Linux, Windows, MacOS or whatever. The only requirement is Python to be already installed.


What's new in this version?
Expand All @@ -30,14 +30,51 @@ What's new in this version?
- Run tests (unit and integration) in Linux, MacOS and Windows.


You can check the whole documentation, including demos and examples, in the docs page:
For more details please [check the documentation](https://pyempaq.readthedocs.io/en/latest/) (which includes demos and examples). The project [exists in Github](https://github.com/facundobatista/pyempaq/).

https://pyempaq.readthedocs.io/en/latest/
Enjoy,

The project:
. Facundo

https://github.com/facundobatista/pyempaq/

Enjoy,
--


Release de PyEmpaq 0.3

Estoy muy contento de anunciar el release de PyEmpaq 0.3.

PyEmpaq es un simple pero poderoso empaquetador de Python para correr cualquier proyecto en cualquier lado.con las dependencias que tenga mientras sean instalables en un entorno virtual.

Con PyEmpaq pueden convertir cualquier proyecto de Python en un archivo único `.pyz`, con todo el contenido del proyecto dentro.

Ese único archivo es todo lo que necesita ser distribuido. Cuando la usuaria final lo ejecute, se expandirá el proyecto original, sus dependencias se instalarán en un entorno virtual, y se correrá. Como no se necesitan permisos o privilegios especiales, todo puede correr en cualquier entorno.

Tanto el empaquetado como la ejecución son completamente multiplataformas. Esto significa que se puede empaquetar el proyecto en Linux, Windows, MacOS o donde sea, y correrá bien en Linux, Windows, MacOS o donde sea. El único requerimiento es tener Python instalado previamente.


¿Qué hay de nuevo en esta versión?

- Agregué opciones 'include' y 'exclude' en la configuración para tener el control completo del contenido del proyecto empaquetado.

- Ahora se reusa el directorio del proyecto existente sólo si se completó satisfactoriamente en una instalación previa.

- Soporta declarar una versión mínima de Python para correr el proyecto empaquetado.

- Se expone el path del archivo `.pyz` al proyecto cuando se ejecuta.

- Ahora usa logging internamente para exponer mensajes a desarrolladores y usuaries.

- Se configura correctamente el PATH en el entorno del comando final.

- Agregué una opción -V/--version al empaquetador para solamente mostrar la versión y salir.

- Los tests (de unidad e integración) se corren automáticamente en Linux, MacOS and Windows.


Para más detalles revisen [la documentación](https://pyempaq.readthedocs.io/en/latest/) (que incluye demos y ejemplos). El proyecto [está en Github](https://github.com/facundobatista/pyempaq/).

Que lo disfruten.

. Facundo

5 changes: 4 additions & 1 deletion pyempaq/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
# collected arguments
Args = namedtuple("Args", "project_name basedir entrypoint requirement_files")

# dependencies needed for the unpacker to run ok
UNPACKER_DEPS = ["appdirs", "packaging"]


def get_pip():
"""Ensure an usable version of `pip`."""
Expand Down Expand Up @@ -208,7 +211,7 @@ def pack(config):
logger.debug("Building internal dependencies dir")
venv_dir = tmpdir / "venv"
pip = get_pip()
cmd = [pip, "install", "appdirs", f"--target={venv_dir}"]
cmd = [pip, "install", *UNPACKER_DEPS, f"--target={venv_dir}"]
logged_exec(cmd)

metadata = prepare_metadata(origdir, config)
Expand Down
16 changes: 9 additions & 7 deletions pyempaq/unpacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
import time
import venv
import zipfile
from types import ModuleType
from typing import List, Dict, Any

from packaging import version

from pyempaq.common import find_venv_bin, logged_exec


Expand Down Expand Up @@ -135,7 +134,7 @@ def setup_project_directory(
(project_dir / COMPLETE_FLAG_FILE).touch()


def restrictions_ok(restrictions: Dict[str, Any]) -> bool:
def restrictions_ok(version: ModuleType, restrictions: Dict[str, Any]) -> bool:
"""Enforce the unpacking restrictions, if any; return True if all ok to continue."""
if not restrictions:
return True
Expand Down Expand Up @@ -165,12 +164,15 @@ def run():
zf = zipfile.ZipFile(pyempaq_filepath)
metadata = json.loads(zf.read("metadata.json").decode("utf8"))
log("Loaded metadata: %s", metadata)
if not restrictions_ok(metadata["unpack_restrictions"]):
exit(1)

# load appdirs from the builtin venv
# load appdirs and packaging from the builtin venv (not at top of file because
# paths needed to be fixed)
sys.path.insert(0, f"{pyempaq_filepath}/venv/")
import appdirs # NOQA: this is an import not at top of file because paths needed to be fixed
import appdirs # NOQA
from packaging import version # NOQA

if not restrictions_ok(version, metadata["unpack_restrictions"]):
exit(1)

pyempaq_dir = pathlib.Path(appdirs.user_data_dir()) / 'pyempaq'
pyempaq_dir.mkdir(parents=True, exist_ok=True)
Expand Down
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

flake8
logassert
packaging
pydocstyle
pytest
pytest-subprocess
13 changes: 7 additions & 6 deletions tests/test_unpacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import pytest
from logassert import Exact, NOTHING
from packaging import version

import pyempaq.unpacker
from pyempaq.unpacker import (
Expand Down Expand Up @@ -228,14 +229,14 @@ def test_projectdir_requirements(tmp_path, logs):
@pytest.mark.parametrize("restrictions", [None, {}])
def test_enforcerestrictions_empty(restrictions, logs):
"""Support for no restrictions."""
ok = restrictions_ok(restrictions)
ok = restrictions_ok(version, restrictions)
assert ok is True
assert NOTHING in logs.any_level


def test_enforcerestrictions_pythonversion_smaller(logs):
"""Enforce minimum python version: smaller version."""
ok = restrictions_ok({"minimum_python_version": "0.8"})
ok = restrictions_ok(version, {"minimum_python_version": "0.8"})
current = platform.python_version()
assert ok is True
assert f"Checking minimum Python version: indicated='0.8' current={current!r}" in logs.info
Expand All @@ -244,7 +245,7 @@ def test_enforcerestrictions_pythonversion_smaller(logs):

def test_enforcerestrictions_pythonversion_bigger_enforced(logs):
"""Enforce minimum python version: bigger version."""
ok = restrictions_ok({"minimum_python_version": "42"})
ok = restrictions_ok(version, {"minimum_python_version": "42"})
current = platform.python_version()
assert ok is False
assert f"Checking minimum Python version: indicated='42' current={current!r}" in logs.info
Expand All @@ -254,7 +255,7 @@ def test_enforcerestrictions_pythonversion_bigger_enforced(logs):
def test_enforcerestrictions_pythonversion_bigger_ignored(logs, monkeypatch):
"""Ignore minimum python version for the bigger version case."""
monkeypatch.setenv("PYEMPAQ_IGNORE_RESTRICTIONS", "minimum-python-version")
ok = restrictions_ok({"minimum_python_version": "42"})
ok = restrictions_ok(version, {"minimum_python_version": "42"})
current = platform.python_version()
assert ok is True
assert f"Checking minimum Python version: indicated='42' current={current!r}" in logs.info
Expand All @@ -266,7 +267,7 @@ def test_enforcerestrictions_pythonversion_bigger_ignored(logs, monkeypatch):
def test_enforcerestrictions_pythonversion_current(logs):
"""Enforce minimum python version: exactly current version."""
current = platform.python_version()
ok = restrictions_ok({"minimum_python_version": current})
ok = restrictions_ok(version, {"minimum_python_version": current})
assert ok is True
assert (
f"Checking minimum Python version: indicated={current!r} current={current!r}" in logs.info
Expand All @@ -276,5 +277,5 @@ def test_enforcerestrictions_pythonversion_current(logs):

def test_enforcerestrictions_pythonversion_good_comparison(logs):
"""Enforce minimum python version using a proper comparison, not strings."""
ok = restrictions_ok({"minimum_python_version": "3.0009"})
ok = restrictions_ok(version, {"minimum_python_version": "3.0009"})
assert ok is True

0 comments on commit 293d432

Please sign in to comment.