{"id":29863462,"url":"https://github.com/miraisolutions/secretsanta","last_synced_at":"2025-07-30T07:10:04.546Z","repository":{"id":49235330,"uuid":"155575891","full_name":"miraisolutions/secretsanta","owner":"miraisolutions","description":"Python package tutorial - secret santa utility","archived":false,"fork":false,"pushed_at":"2025-07-29T13:49:18.000Z","size":577,"stargazers_count":5,"open_issues_count":2,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-07-29T15:54:47.379Z","etag":null,"topics":["continuous-integration","jupyter","pypi","python","sphinx","tox"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/miraisolutions.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-10-31T14:59:09.000Z","updated_at":"2025-07-08T08:38:08.000Z","dependencies_parsed_at":"2024-02-28T13:28:48.851Z","dependency_job_id":"ae43598c-1e69-48a6-923a-a5bbf4a77e46","html_url":"https://github.com/miraisolutions/secretsanta","commit_stats":{"total_commits":186,"total_committers":7,"mean_commits":"26.571428571428573","dds":0.6827956989247312,"last_synced_commit":"dd32290020eafef51cabf102725663e226140ee1"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/miraisolutions/secretsanta","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraisolutions%2Fsecretsanta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraisolutions%2Fsecretsanta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraisolutions%2Fsecretsanta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraisolutions%2Fsecretsanta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/miraisolutions","download_url":"https://codeload.github.com/miraisolutions/secretsanta/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraisolutions%2Fsecretsanta/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267719318,"owners_count":24133467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["continuous-integration","jupyter","pypi","python","sphinx","tox"],"created_at":"2025-07-30T07:10:00.565Z","updated_at":"2025-07-30T07:10:04.423Z","avatar_url":"https://github.com/miraisolutions.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# secretsanta\n\nThis repository implements a basic Python version of a [Secret Santa](https://en.wikipedia.org/wiki/Secret_Santa)\nutility. It is meant to serve as a tutorial for beginners interested in Python package development.\nEach section below mentions typical tools and utilities in a natural order of developing Python packages.\n\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/secretsanta.svg)](https://pypi.python.org/pypi/secretsanta)\n[![PyPI](https://img.shields.io/pypi/v/secretsanta.svg)](https://pypi.python.org/pypi/secretsanta)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/secretsanta.svg)](https://pypistats.org/packages/secretsanta)\n[![PyPI - License](https://img.shields.io/pypi/l/secretsanta.svg)](https://pypi.python.org/pypi/secretsanta)\n\n[![Build Status](https://github.com/miraisolutions/secretsanta/actions/workflows/python-package.yml/badge.svg)](https://github.com/miraisolutions/secretsanta/actions/workflows/python-package.yml)\n\n[![codecov](https://codecov.io/gh/miraisolutions/secretsanta/branch/master/graph/badge.svg)](https://codecov.io/gh/miraisolutions/secretsanta)\n\n## Table of Contents\n\n1. [Development](#development)  \n    a. [Virtual environments](#virtual-environments)  \n    b. [Project requirements \u0026 environment setup](#project-requirements--environment-setup)  \n2. [Testing](#testing)  \n    a. [Running tests with Nox](#running-tests-with-nox)  \n    b. [PyCharm file types](#pycharm-file-types)  \n    c. [Type hints](#type-hints)  \n    d. [Property testing](#property-testing)  \n    e. [Mocks in unit tests](#mocks-in-unit-tests)  \n3. [Documentation](#documentation)  \n    a. [Building docs with Nox](#building-docs-with-nox)  \n4. [Usage](#usage)  \n    a. [Jupyter notebook](#jupyter-notebook)  \n    b. [Command-line interface](#command-line-interface-cli)  \n    c. [Package installation \u0026 CLI](#package-installation--cli)  \n5. [Continuous integration](#continuous-integration)  \n6. [Miscellaneous](#miscellaneous)  \n\n## Development\n\nWe assume **PyCharm** on **Ubuntu \u003e= 20.04** as the development environment, but you might as well use a newer Linux version or even Windows instead.\n\nIn PyCharm, check out this repository into a new project, e.g. under\n\n`VCS \u003e Checkout from Version Control`\n\nShell commands below should be entered in the **Terminal** pane of PyCharm.\n\n*There is no shortcut in PyCharm to send code from the editor to the terminal, so you need to copy-paste commands instead.*\n\n[//]: # \"(I tried both *Quick Lists* and *Macros* but neither seems exactly fit for this purpose.)\"\n[//]: # \"This is a comment. See https://stackoverflow.com/questions/4823468/comments-in-markdown\"\n\n### Project requirements \u0026 Environment Setup\n\nThis project uses [uv](https://github.com/astral-sh/uv) for dependency management and [Nox](https://nox.thea.codes/) for task automation and testing across multiple Python versions.\n\n**Important:** make sure all commands are executed inside the virtual environment, e.g. at such a prompt:\n\n```bash\n#\u003e (venv) localuser@Ubuntu:~/PyCharm/secretsanta$\n```\n\nFirst, ensure you have `uv` installed. You can install them into your global Python environment or use `pipx`:\n\n```bash\npip install uv\n# or\npipx install uv\n```\n\nSee also [other installation options](https://docs.astral.sh/uv/getting-started/installation/)\n\nCheck that `uv` has been installed, and its version:\n\n```bash\nuv --version\n```\n\nTo set up your development environment, synchronize it with the locked dependencies specified in `uv.lock`:\n\n```bash\n# Install runtime, dev, test, and docs dependencies\nuv sync --dev\n```\n\nYou can add dependencies with `uv add some_package`, optionally with a version specifier (e.g. `uv add some_package\u003e=1.2.3`).\nThis will modify `pyproject.toml` and `uv.lock` and re-sync the environment.\n\nIf you modify dependencies in `pyproject.toml`, update the lock file separately:\n\n```bash\nuv lock\n```\n\nThen re-sync your environment:\n\n```bash\nuv sync --dev\n```\n\nYou can also run commands within the managed environment using `uv run`:\n\n```bash\nuv run -- python secretsanta/cli/cli.py --help\n```\n\n### Virtual environments\n\nA virtual environment for the project is created automatically by `uv sync`. This keeps the global Python environment clean.\nA couple of useful references about virtual environments if you've never used them before:\n\n* [Virtual environments](https://docs.python-guide.org/dev/virtualenvs/)\n* [Creating virtual environments](https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments)\n\nConfigure the PyCharm project with the project's Python virtual environment under\n\n`File \u003e Settings \u003e Project: secretsanta \u003e Python Interpreter`\n\nClick on `Add Interpreter` and select `Add Local Interpreter`, then choose `Select Existing`, using `\u003cPROJECT_PATH\u003e/.venv/bin/python`\nas the path.\n\n*We do not use `pipenv` here. You may however use it to create a new environment\n[in a similar way](https://www.jetbrains.com/help/pycharm/pipenv.html#pipenv-existing-project).*\n\nWith these settings, anything you execute within the PyCharm project, either at the Terminal or in the Python Console,\nwill run in the virtual environment. Close and re-open PyCharm to make sure the settings are picked up.\n\nNote that you can still temporarily leave the virtual environment from an active Terminal using\n\n```bash\ndeactivate\n```\n\nand re-activate it using\n\n```bash\nsource ./venv/bin/activate\n```\n\nYou can also switch to a different project interpreter in PyCharm (Ctrl + Shift + A, search for `Switch Project Interpreter`).\nOpen terminals and Python consoles then need to be restarted for the environment to match the project interpreter.\n\n## Testing\n\nThere are multiple ways to define and execute tests. Two of the most common ones are `doctest` and `unittest`.\n\nThe `doctest` module allows to run code examples / tests that are defined as part of `docstrings`.\n\nUse the following command to see this in action. The `-v` flag allows us to see verbose output.\nIn case everything is fine, we would not see any output otherwise.\n\n```{bash, eval=FALSE}\nuv run python -m doctest secretsanta/main/core.py -v\n# Or run via nox (included in the 'tests' session)\nuv run nox -s tests -- -m doctest secretsanta/main/core.py -v\n```\n\nIt is possible to run code style checks with [ruff](https://docs.astral.sh/ruff/):\n\n```{bash, eval=FALSE}\n# Run directly\nuv run ruff check\nuv run ruff format --check\n# Or run via nox\nuv run nox -s lint\n```\n\nIf all is fine, you will not see any output from `ruff` directly. `nox` will report success.\n\nUnit tests are kept under `tests`.\n\n### Running Tests with Nox\n\n[Nox](https://nox.thea.codes/) is used to automate testing across multiple Python versions (defined in `noxfile.py`).\n\nList available Nox sessions:\n\n```bash\nuv run nox --list\n```\n\nRun all test sessions (for Python 3.9, 3.10, 3.11, 3.12):\n\n```bash\nuv run nox -s tests\n```\n\nRun tests for a specific Python version:\n\n```bash\nuv run nox -s tests-3.10\n```\n\nRun linting session:\n\n```bash\nuv run nox -s lint\n```\n\nRun all sessions marked as default:\n\n```bash\nuv run nox\n```\n\nNox handles creating temporary virtual environments for each session, installing dependencies using `uv`, and running the specified commands. Test coverage is measured using `pytest-cov` (see `.coveragerc` and `pyproject.toml` for configuration).\n\n### PyCharm file types\n\nIn PyCharm, you can associate files to a certain type under:\n\n`File \u003e Settings \u003e Editor \u003e File Types`\n\nE.g. use this to get `.coveragerc` marked up as `INI` (you can do this after installing the .ini support PyCharm plugin).\nAlternatively, you can register the `*.ini` and `.coveragerc` patterns to the *existing* **Buildout Config**\n[file type](https://intellij-support.jetbrains.com/hc/en-us/community/posts/206585245/comments/205965729).\n\n### Type hints\n\nType hints define what type function arguments and return values should be. They are both a source of documentation\nand testing framework to identify bugs more easily, see also [PEP 484](https://www.python.org/dev/peps/pep-0484/).\n\nmypy comes installed via `uv sync --dev`.\n\nRun something like below:\n\n```{bash, eval=FALSE}\nuv run mypy ./secretsanta/main/core.py\nuv run mypy ./tests\nuv run mypy .\n# Or run via nox (if a session is added)\n# nox -s typecheck\n```\n\nto test if the type hints of `.py` file(s) are correct (in which case it would typically output a \"Success\" message).\n\n### Property testing\n\nWe use [Hypothesis](https://hypothesis.readthedocs.io/en/latest/) to define a *property test* for our matching function:\ngenerated example inputs are tested against desired properties. Hypothesis' generator can be configured to produce typical\ndata structures, filled with various instances of primitive types. This is done by composing specific annotations.\n\n* The decorator `@given(...)` must be present before the test function that shall use generated input.\n* Generated arguments are defined in a comma-separated list, and will be passed to the test function in order:\n\n```python\nfrom hypothesis import given\nfrom hypothesis.strategies import text, integers\n\n\n@given(text(), integers())\ndef test_some_thing(a_string, an_int):\n    return\n\n```  \n\n* Generation can be controlled by various optional parameters, e.g. `text(min_size=2)` for testing with strings that\nhave at least 2 characters.\n\n### Mocks in unit tests\n\nMock objects are used to avoid external side effects. We use the standard Python package `unittest.mock`. This provides\na `@patch` decorator, which allows us to specify classes to be mocked within the scope of a given test case. See\n*test_funs.py* and *test_core.py* for examples.\n\n## Documentation\n\nDocumentation is done using [Sphinx](http://www.sphinx-doc.org/en/master/usage/quickstart.html). We use Google style docstrings as that seems to be prevalent in the industry,\nwith the addition of `napoleon` Sphinx extension.\n\nThe required dependencies, defined in pyproject.toml (e.g. Sphinx) are installed via `uv sync --dev`.\n\n### Initializing documentation - already done - for reference\n\n```{bash, eval=FALSE}\nsphinx-quickstart\n```\n\nThis will lead through an interactive generation process.\n\nSuggested values / options are listed here.\nHitting enter without typing anything will take the suggested default shown inside square brackets [ ].\n\n* Root path for the documentation [.]: docs\n* Separate source and build directories (y/n) [n]: y\n* Name prefix for templates and static dir[_]: Enter\n* Project name: secretsanta\n* Author name(s): Mirai Solutions\n* Project version[]: 0.1\n* Project release[0.1]: 0.1.1\n* Project language [en]: None\n* Source file suffix [.rst]: .rst\n* Name of your master document (without suffix) [index]: Enter\n* Do you want to use epub builder (y/n) [n]: n\n* autodoc: automatically insert docstrings from modules (y/n) [n]: y\n* doctest: automatically test code snippets in doctest blocks (y/n) [n]: y\n* intersphinx: link between Sphinx documentation of different projects (y/n) [n]: y\n* todo: write \"todo\" entries that can be shown or hidden on build (y/n) [n]: n\n* coverage: checks for documentation coverage (y/n) [n]: y\n* imgmath: include math, rendered as PNG or SVG images (y/n) [n]: n\n* mathjax: include math, rendered in the browser by MathJax (y/n) [n]: y\n* ifconfig: conditional inclusion of content based on config values (y/n) [n]: n\n* viewcode: include links to the source code of documented Python objects (y/n) [n]: y\n* githubpages: create .nojekyll file to publish the document on GitHub pages (y/n) [n]: n\n* Create Makefile? (y/n) [y]: y\n* Create Windows command file? (y/n) [y]: n\n\nIn order to use `autodoc`, one needs to uncomment the corresponding line in `docs/source/conf.py`:\n\n```sys.path.insert(0, os.path.abspath(...```\n\nAnd set the appropriate path to the directory containing the modules to be documented.\n\n*For Sphinx/autodoc to work, the docstrings must be written in correct\n[reStructuredText](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html),\nsee [documentation](https://pythonhosted.org/an_example_pypi_project/sphinx.html#auto-directives) for details.*\n\n### Building Docs with Nox\n\nUse Nox to build the documentation:\n\n```bash\nuv run nox -s docs\n```\n\nThis command runs `sphinx-build` in a dedicated environment managed by Nox.\n\nYou can view the documentation by opening `docs/build/html/index.html` in your browser.\n\n*Previewing the .rst files directly in PyCharm might not render Sphinx directives correctly.*\n\n## Usage\n\n### Jupyter Notebook\n\nThe [Jupyter](https://jupyter.org/) notebook `SecretSanta.ipynb` illustrates the usage of the `secretsanta` package.\n\nIt can be run in your browser (or directly in PyCharm if you have the professional edition):\n\n```bash\njupyter notebook SecretSanta.ipynb\n```\n\nBelow gives you some useful information about the location of `Jupyter` related directories, e.g. configuration:\n\n```bash\njupyter --path\n```\n\nAdditionally, you can open and run SecretSanta.ipynb in vs code, provided:\n\n* you have the `Jupyter` extension installed\n* you add the jupyter dependencies to your development environment: `uv sync --all-groups`\n\n\u003c!-- e.g.: `etc/jupyter/custom/custom.js` --\u003e\n\nA few additional links to some typical early `Jupyter` topics:\n\n* [Closing running Jupyter notebook servers](https://github.com/jupyter/notebook/issues/2844)\n* [Checkpoints and autosave](https://groups.google.com/forum/#!topic/jupyter/DGCKE5fS4kQ)\n\n### Command-line Interface (CLI)\n\nPython's ecosystem offers several ways to tackle command-line interfaces. The traditional standard method is to use\nthe `argparse` module that is part of the standard library. This can be complemented by something like `argparsetree`\nfor larger and more complex command-line applications.\n\nHere we have chosen to use [Click](https://click.palletsprojects.com/) instead, which allows us to define our CLI via\ndecorated functions in a neat and compact way. Other potential alternatives could\nbe [docopt](https://docopt.readthedocs.io/) or [Invoke](https://www.pyinvoke.org/).\n\nA nice comparison is\navailable [here](https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/).\n\nIn order to run the CLI commands during development, use `uv run`:\n\n```bash\nuv run -- santa --help\nuv run -- santa makedict --help\nuv run -- santa makedict \"./validation/participants.json\"\n```\n\nAlternatively, activate your virtual environment (where dependencies are installed via `uv sync`) and run directly:\n\n```bash\n# Assuming your venv is activated\nsanta --help\nsanta makedict \"./validation/participants.json\"\n```\n\n### Package Installation \u0026 CLI\n\nIf you install the package, you can use the CLI tool as designed for the end user:\n\n#### Build the package wheel\n\n```bash\nuv build --wheel # creates build and dist directories\n```\n\n#### Install in a new project / environment\n\n##### On Windows\n\n```cmd\nuv init # creates a new uv project\nuv add ..\\secretsanta\\dist\\secretsanta-0.1.0-py3-none-any.whl\n# if already installed, delete the old uv.lock first\nrm uv.lock\nuv add ..\\secretsanta\\dist\\secretsanta-0.1.0-py3-none-any.whl\n```\n\n##### On Ubuntu\n\n```bash\nuv init # creates a new uv project\nuv add ../secretsanta/dist/secretsanta-0.1.0-py3-none-any.whl\n# if already installed, delete the old uv.lock first\nrm uv.lock\nuv add --force-reinstall ./dist/secretsanta-0.1.0.tar.gz\n```\n\n#### Use the CLI tool\n\n```sh\nuv run santa --help\nuv run santa makedict --help\nuv run santa makedict \"./validation/participants.json\"\n```\n\n## Continuous Integration\n\nContinuous Integration (CI) aims to keep state updated to always match the code currently checked in a repository.\nThis typically includes a build, automated test runs, and possibly making sure that the newly built artifacts are\ndeployed to a target environment. This helps developers and users by providing timely feedback and showing what the\nresults of certain checks were on a given version of the code.\n\nWe use GitHub Actions to implement CI. Building and checking the package is implemented in [python-package.yml](./.github/workflows/python-package.yml). This includes running tests and code linting / formatting checks.\n\nCoverage information is generated and uploaded to [codecov](https://codecov.io/), which generates a\n[report](https://codecov.io/gh/miraisolutions/secretsanta) out of it.\n\nBuild status and coverage reports are linked via badges at the top of this README.\n\nCode scanning for security is performed using CodeQL ([codeql.yml](.github/workflows/codeql.yml)).\n\nDependency updates are managed by Dependabot (see [dependabot.yml](.github/dependabot.yml)).\n\nCodecov is configured in [codecov.yml](./codecov.yml), defining the coverage value range (in percent) to match to a color scale, as\nwell as the coverage checks to be performed and their success criteria. See codecov's\n[general configuration](https://docs.codecov.io/docs/codecov-yaml) and\n[commit status evaluation](https://docs.codecov.io/docs/commit-status) documentation for more information.\n\n*Notifications from codecov can only be delivered via unencrypted webhook URLs. In order to avoid exposing such hooks in\na public repository, we do not use this functionality here.*\n\n## Miscellaneous\n\n* `MANIFEST.in` specifies extra files that shall be included in a source distribution.\n* Badges: This README features various badges (at the beginning), including a build status badge and a code coverage\nstatus badge.\n\n### Logging\n\nThe `logging` package is used to track events after running the project. The main logged events (levels) in Secret Santa are: errors, warnings, and participants info. A log level is set as an environment variable, e.g.:\n\n```bash\nos.environ[\"level\"] = \"ERROR\"\n```\n\nAll logs activities are collected into a log file that is initiated at the beginning of the code:\n\n```bash\nlogging.basicConfig(filename = path_to_file, level = level, format = '%(asctime)s %(levelname)s %(message)s',\n                               datefmt = '%Y/%m/%d %I:%M:%S %p')\n```\n\nA logger is then set:\n\n```bash\nlogger = logging.getLogger(__name__)\n```\n\nAll functions used afterwards refer to this logger:\n\n```bash\nlogger.error(\"Error message\")\nlogger.warning(\"Warning message\")\nlogger.info(\"Info\")\n```\n\nThe log file is automatically created in the `log_files` directory and can be inspected after the project run is complete.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiraisolutions%2Fsecretsanta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmiraisolutions%2Fsecretsanta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiraisolutions%2Fsecretsanta/lists"}