# Mystery

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/mystery.svg)](https://pypi.python.org/pypi/mystery/)
[![PyPI version fury.io](https://badge.fury.io/py/mystery.svg)](https://pypi.python.org/pypi/mystery/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)

> "It is a riddle, wrapped in a mystery, inside an enigma."
>
> -- _Sir Winston Churchill_

Mystery is a Python package that is instantiated as a _different_ package every time you install it!

It was inspired by [this episode](https://pythonbytes.fm/episodes/show/116/so-you-want-python-in-a-3d-graphics-engine) of the wonderful podcast [Python Bytes](https://pythonbytes.fm/) (which in turn talked about [this article](https://dustingram.com/articles/2018/03/05/why-pypi-doesnt-know-dependencies/) regarding pip dependencies).

Also, a shoutout to [this](https://github.com/hugovk/top-pypi-packages) project by [hugovk](https://github.com/hugovk) that creates dumps of the top downloaded packages in [PyPI](https://pypi.org/) every week. Without it's existence, being able to randomly choose a top package from PyPI would've been a lot less fun.

## Installation

Use the package manager [pip](https://pip.pypa.io/en/stable/) to install mystery.

```bash
pip install --no-cache-dir mystery
```

The reason the `--no-cache-dir` flag is used is the [random package choice process](##How-does-it-work?). The dynamically created local instance of the mystery package is simply getting cached so without disabling the caching process the same chosen package will be used in the next reinstallation.

## Usage

```python
import mystery  # Who knows what's inside?

# `mystery` is now some random package!

mystery.__name__  # Quick cheat if you really are curious ;)
```

## How does it work?

Mystery doesn't come with it's own `__init__.py` (well actually it does but it's only for setuptools to work, and even this one is also autogenerated when I distribute the package!).

What happens behind the scenes is that a `__init__.py` file is being _dynamically generated_ when the package is built and installed. Mystery uses either the newest online dump of the top downloaded PyPI packages stored in [this repository](https://github.com/hugovk/top-pypi-packages) or (in case the repository was unreachable) the backup [offline dump](top-pypi-packages-30-days.min.json) that comes with mystery's distribution.

After choosing a package, mystery's `setup.py` will create a **lockfile** in the temporary directory that will store the chosen package's name. The reason for that is that pip will actually run `setup.py` _twice_: first when building the package (`build_py` command) and again when actually installing the package (`install` command). The lockfile will hence be used after it's creation (first run of `setup.py`) to get the chosen package's name out of it (so the `install_requires` parameter in the two calls to `setuptools.setup()` will be in sync) and will then be deleted.

> **Note** that using a lockfile means that if for some reason the lockfile's state gets out of sync (perhaps the setup process is stopped right after the build phase created the lockfile but before the installation phase deleted it) the next attempts at installing mystery will also **fail**. You can fix that by **manually deleting the lockfile** from your filesystem (it's location is stored in [this configuration file](config.json)).

## License

[MIT](LICENSE.txt)
