diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5c1af9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,228 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0a2168f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Comparing Hatch, Poetry, and Rye + +* [hatch-demo](./hatch-demo/README.md) diff --git a/hatch-demo/README.md b/hatch-demo/README.md new file mode 100644 index 0000000..105f4c8 --- /dev/null +++ b/hatch-demo/README.md @@ -0,0 +1,105 @@ +# Hatch Demo + +## Steps + +1. Install `hatch` + +```bash +$ pipx install hatch +# or +$ brew install pipx +``` + +2. Create new project +```bash +$ hatch new "Hatch Demo" +$ cd hatch-demo +``` + +3. Create default environment +```bash +$ hatch env create +``` + +4. Set `uv` as the default installer by adding this to `pyproject.toml`: + +```toml +[tool.hatch.envs.default] +installer = "uv" +``` + +5. Add dependencies (`requests`, `polars`, `pytest`, `ruff`) + * Note 1: I didn't see a way to add deps via `hatch` command, so this was manually done by editing `pyproject.toml`. (This seems to be the procedure described in [their docs](https://hatch.pypa.io/dev/environment/#dependencies).) + * Note 2: Add `pytest` and `ruff` under the `dev` environment. + +```toml +[tool.hatch.envs.default] +installer = "uv" +dependencies = ["polars", "requests"] + +[tool.hatch.envs.dev] +installer = "uv" +dependencies = [ + "hatch-demo", + "pytest", + "ruff" +] +``` +_Note: You have to reference `hatch-demo` in order to get the project deps needed for running tests._ + +6. Add `test` and `lint` scripts to `dev` env + +```toml +[tool.hatch.envs.dev.scripts] +test = "pytest" +lint = "ruff check --fix && ruff format" +``` + +7. Create/install the env + +```bash +$ hatch env create dev +``` + +8. Add code (`demo.py`) and tests (`test_demo.py`) + +9. Check envs + +```bash +$ hatch env show + Standalone +┏━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━┓ +┃ Name ┃ Type ┃ Dependencies ┃ Scripts ┃ +┡━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━┩ +│ default │ virtual │ polars │ run │ +│ │ │ requests │ │ +├─────────┼─────────┼──────────────┼─────────┤ +│ dev │ virtual │ pytest │ lint │ +│ │ │ ruff │ run │ +│ │ │ │ test │ +└─────────┴─────────┴──────────────┴─────────┘ +``` + +10. Run tests and lint/format code + +```bash +$ hatch run dev:test +$ hatch run dev:lint +``` + +11. Add default script to run the app + +```toml +[tool.hatch.envs.default.scripts] +run = "python src/hatch_demo/demo.py" +``` + +12. Run the app +```bash +$ hatch run default:run +``` + +## Notes + +* Not sure how to install the project so that it can called like `python -m hatch_demo` +* Not sure how to call the entrypoint scripts \ No newline at end of file diff --git a/hatch-demo/pyproject.toml b/hatch-demo/pyproject.toml new file mode 100644 index 0000000..1c8d7aa --- /dev/null +++ b/hatch-demo/pyproject.toml @@ -0,0 +1,52 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "hatch-demo" +dynamic = ["version"] +description = '' +readme = "README.md" +requires-python = ">=3.12" +license = "MIT" +keywords = [] +authors = [ + { name = "David Kun", email = "davidkun4@gmail.com" }, +] +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +dependencies = [] + +[project.urls] +Source = "https://github.com/davidkun/hatch-vs-poetry-vs-rye" + +[tool.hatch.version] +path = "src/hatch_demo/__about__.py" + +[tool.hatch.envs.default] +installer = "uv" +dependencies = [ + "polars", + "requests" +] + +[tool.hatch.envs.default.scripts] +run = "python src/hatch_demo/demo.py" + +[tool.hatch.envs.dev] +installer = "uv" +dependencies = [ + "hatch-demo", + "pytest", + "ruff" +] + +[tool.hatch.envs.dev.scripts] +test = "pytest" +check = "ruff check" +lint = "ruff check --fix && ruff format" \ No newline at end of file diff --git a/hatch-demo/src/hatch_demo/__about__.py b/hatch-demo/src/hatch_demo/__about__.py new file mode 100644 index 0000000..f102a9c --- /dev/null +++ b/hatch-demo/src/hatch_demo/__about__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/hatch-demo/src/hatch_demo/__init__.py b/hatch-demo/src/hatch_demo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hatch-demo/src/hatch_demo/__main__.py b/hatch-demo/src/hatch_demo/__main__.py new file mode 100644 index 0000000..c70a4b8 --- /dev/null +++ b/hatch-demo/src/hatch_demo/__main__.py @@ -0,0 +1,4 @@ +from demo import main + +if __name__ == "__main__": + main() diff --git a/hatch-demo/src/hatch_demo/demo.py b/hatch-demo/src/hatch_demo/demo.py new file mode 100644 index 0000000..a88df86 --- /dev/null +++ b/hatch-demo/src/hatch_demo/demo.py @@ -0,0 +1,26 @@ +from io import StringIO +import requests +import polars as pl + +IRIS = "https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv" + + +def main(): + result = analyze(dataframe_from(iris_data())) + print(result) + + +def iris_data(url: str = IRIS) -> str: + return requests.get(url).text + + +def dataframe_from(csv_data: str) -> pl.DataFrame: + return pl.read_csv(StringIO(csv_data)) + + +def analyze(df: pl.DataFrame) -> pl.DataFrame: + return df.filter(pl.col("sepal_length") > 5).group_by("species").agg(pl.all().sum()) + + +if __name__ == "__main__": + main() diff --git a/hatch-demo/tests/__init__.py b/hatch-demo/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hatch-demo/tests/test_demo.py b/hatch-demo/tests/test_demo.py new file mode 100644 index 0000000..c25f476 --- /dev/null +++ b/hatch-demo/tests/test_demo.py @@ -0,0 +1,9 @@ +from hatch_demo.demo import dataframe_from + + +def test_dataframe_from(): + df = dataframe_from( + """A,B,C + 0.0,10.0,200.1""" + ) + assert len(df.count()) == 1