Repository: amogorkon/justuse (language: python)

Issue:
Aspectize should be more user-friendly with decorating methods

Aspectize should be more user-friendly with decorating methods

Finally think we've smoothed out enough rough edges to warrant a new release (Sourcery refactored)
Pull Request #463 refactored by [Sourcery](https://sourcery.ai/github/).

If you're happy with these changes, merge this Pull Request using the *Squash and merge* strategy.

**NOTE**: As code is pushed to the original Pull Request, Sourcery will
re-run and update (force-push) this Pull Request with new refactorings as
necessary. If Sourcery finds no refactorings at any point, this Pull Request
will be closed automatically.

See our documentation [here](https://docs.sourcery.ai/GitHub/Using-Sourcery-for-GitHub/).

<details>
<summary>Run Sourcery locally</summary>
<p>
Reduce the feedback loop during development by using the Sourcery editor plugin:
</p>
<ul>
<li><a href="https://sourcery.ai/download/?editor=vscode">VS Code</a></li>
<li><a href="https://sourcery.ai/download/?editor=pycharm">PyCharm</a></li>
</ul>
</details>

<details>
<summary>Review changes via command line</summary>
<p>To manually merge these changes, make sure you're on the <code>unstable</code> branch, then run:</p>
<pre>
git fetch origin sourcery/unstable
git merge --ff-only FETCH_HEAD
git reset HEAD^
</pre>
</details>

Help us [improve](https://research.typeform.com/to/j06Spdfr?type=pr_refactor&github_login=amogorkon&base_repo=https%3A%2F%2Fgithub.com%2Famogorkon%2Fjustuse.git&base_remote_ref=refs%2Fpull%2F463%2Fhead&base_ref=unstable&base_sha=70634d6d5c1a73edaa8ff0967356972a7206953d&head_repo=https%3A%2F%2Fgithub.com%2Famogorkon%2Fjustuse.git&head_ref=sourcery%2Funstable&base_pr_number=463&base_pr_state=open) this pull request!
pebkac cases should result in an html page with an overview of what's available. the user then should select what they need

use(use.Path()) shouldn't require "valid" .py filenames
on jupyter, Ubuntu:

```
test = use(use.Path("../src/.test4.py"))

Last executed at 2021-11-23 00:12:35 in 54ms

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-30-37ec083fbdba> in <module>
----> 1 test = use(use.Path("../src/.test4.py"))

~/anaconda3/lib/python3.8/site-packages/use/use.py in wrapper(*args, **kw)
    278 
    279     def wrapper(*args, **kw):
--> 280         return dispatcher.dispatch(args[1].__class__)(*args, **kw)
    281 
    282     wrapper.register = dispatcher.register

~/anaconda3/lib/python3.8/site-packages/use/use.py in _use_path(self, path, initial_globals, as_import, default, aspectize, modes)
   1797                 self._set_mod(name=name, mod=mod, frame=frame)
   1798                 try:
-> 1799                     mod = _build_mod(
   1800                         name=name,
   1801                         code=code,

~/anaconda3/lib/python3.8/site-packages/typeguard/__init__.py in wrapper(*args, **kwargs)
   1013         memo = _CallMemo(python_func, _localns, args=args, kwargs=kwargs)
   1014         check_argument_types(memo)
-> 1015         retval = func(*args, **kwargs)
   1016         try:
   1017             check_return_type(retval, memo)

~/anaconda3/lib/python3.8/site-packages/use/use.py in _build_mod(name, code, initial_globals, module_path, aspectize, aspectize_dunders)
   1190 ) -> ModuleType:
   1191 
-> 1192     package_name, rest = _parse_name(name)
   1193     mod = ModuleType(rest)
   1194 

~/anaconda3/lib/python3.8/site-packages/typeguard/__init__.py in wrapper(*args, **kwargs)
   1013         memo = _CallMemo(python_func, _localns, args=args, kwargs=kwargs)
   1014         check_argument_types(memo)
-> 1015         retval = func(*args, **kwargs)
   1016         try:
   1017             check_return_type(retval, memo)

~/anaconda3/lib/python3.8/site-packages/use/use.py in _parse_name(name)
    643 def _parse_name(name) -> Tuple[str, str]:
    644     match = re.match(r"(?P<package_name>[^.]+)\.?(?P<rest>[a-zA-Z0-9._]+)?", name)
--> 645     assert match, f"Invalid name spec: {name!r}"
    646     names = match.groupdict()
    647     return names["package_name"], names["rest"] or name

AssertionError: Invalid name spec: '.test4'
```
use() should raise a NirvanaWarning and give the user some wisdom about the meaning of emptyness

We need a use.no_browser flag

case 0010 fails with "discord.py"
```
python -m pip install discord.py
use("discord.py")
```
leads to
``` 
Traceback (most recent call last):
  File "F:\Dropbox (Privat)\code\justuse\src\.test5.py", line 3, in <module>
    mod  = use("discord.py")
  File "F:\Dropbox (Privat)\code\justuse\src\use\main.py", line 113, in __call__
    return self.__implementation(*args, **kwargs)
  File "F:\Dropbox (Privat)\code\justuse\src\use\tools.py", line 46, in wrapper
    return dispatcher.dispatch(args[1].__class__)(*args, **kwargs)
  File "F:\Dropbox (Privat)\code\justuse\src\use\main.py", line 728, in _use_str
    return self._use_package(
  File "G:\Python398\lib\site-packages\icontract\_checkers.py", line 641, in wrapper
    result = func(*args, **kwargs)
  File "F:\Dropbox (Privat)\code\justuse\src\use\main.py", line 801, in _use_package
    result = buffet_table(case, kwargs)
  File "F:\Dropbox (Privat)\code\justuse\src\use\buffet_old.py", line 40, in buffet_table
    result = case_func()
  File "F:\Dropbox (Privat)\code\justuse\src\use\buffet_old.py", line 21, in <lambda>
    (0, 0, 1, 0): lambda: pimp._import_public_no_install(**kwargs),
  File "F:\Dropbox (Privat)\code\justuse\src\use\pimp.py", line 376, in _import_public_no_install
    if mod is None:
UnboundLocalError: local variable 'mod' referenced before assignment
```
we need a registry_clean_slate() to re-create the registry if it failed to load for some reason

Implement call-specific messages in TupleMessage, StrMessage, KwargMessage

different paths (envs, packages, logs) need to be configurable

invalid literal for int() with base 16: 'Y復㝿浯䨩䩯鷛㬉鼵爔滥哫鷕 逮 愁墕萮緩' (spaces in hashes)
First I did:
```py
>>> use("package-example", version="0.1", modes=use.use.auto_install)
```

output was:

```
>>> INFO: use.main: case = (True, False, False, True)
DEBUG: urllib3.connectionpool: Starting new HTTPS connection (1): pypi.org:443
DEBUG: urllib3.connectionpool: https://pypi.org:443 "GET /pypi/package-example/json HTTP/1.1" 200 750
...
`use("package-example", version="0.1", hashes={'Y復㝿浯䨩䩯鷛㬉鼵爔滥哫鷕逮 愁墕萮緩'}, modes=use.auto_install)`
```

So then, upon running the suggestion, I get:
```py
>>> use("package-example", version="0.1", hashes={'Y復㝿浯䨩䩯鷛㬉鼵爔滥哫鷕逮 愁墕萮緩'}, modes=use.auto_install)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/data/media/0/src/use/src/src/use/main.py", line 118, in __call__
    return self.__implementation(*args, **kwargs)
  File "/data/media/0/src/use/src/src/use/tools.py", line 46, in wrapper
    return dispatcher.dispatch(args[1].__class__)(*args, **kwargs)
  File "/data/media/0/src/use/src/src/use/main.py", line 704, in _use_str
    return self._use_package(
  File "/usr/local/lib/python3.9/dist-packages/icontract/_checkers.py", line 641, in wrapper
    result = func(*args, **kwargs)
  File "/data/media/0/src/use/src/src/use/main.py", line 743, in _use_package
    hashes: set[int] = {JACK_as_num(H) if is_JACK(H) else int(H, 16) for H in hashes}
  File "/data/media/0/src/use/src/src/use/main.py", line 743, in <setcomp>
    hashes: set[int] = {JACK_as_num(H) if is_JACK(H) else int(H, 16) for H in hashes}
ValueError: invalid literal for int() with base 16: 'Y復㝿浯䨩䩯鷛㬉鼵爔滥哫鷕 逮 愁墕萮緩'
```


Discussion / hints:
**Yay for this!** As a terse and insufficient follow-up to beartype/beartype#73, I was thinking of maybe a decorator-via-aspectization `justuse` API resembling:

``` python
from beartype import beartype
from justuse import use

# A "muh_package" proxy transitively decorating all callables
# whose fully-qualified names match "muh_package.*" with
# the @beartype decorator. It feels good, man.
muh_package_beartyped = use(
    'muh_package',
    aspectize_package_callables_with_decorator=beartype,  # <-- heh
)
```

As parameter names go, `aspectize_package_callables_with_decorator` is longer than Satan's grubby beard. That's bad. But that's the name we're going with! You previously mentioned pattern matching via an arbitrary boolean tester (e.g., `inspect.isfunction()`), which suggests you have more general-purpose ideas in mind. That's good.

Let's general-purpose that API up. I leave this in your ingenious Eurozone hands. May we all survive the brewing pandemic hellstorm and live to tell the tale over thick and pungent tiergarten lager.
**Yay for this!** As a terse and insufficient follow-up to beartype/beartype#73, I was thinking of maybe a decorator-via-aspectization `justuse` API resembling:

``` python
from beartype import beartype
from justuse import use

# A "muh_package" proxy transitively decorating all callables
# whose fully-qualified names match "muh_package.*" with
# the @beartype decorator. It feels good, man.
muh_package_beartyped = use(
    'muh_package',
    aspectize_package_callables_with_decorator=beartype,  # <-- heh
)
```

As parameter names go, `aspectize_package_callables_with_decorator` is longer than Satan's grubby beard. That's bad. But that's the name we're going with! You previously mentioned pattern matching via an arbitrary boolean tester (e.g., `inspect.isfunction()`), which suggests you have more general-purpose ideas in mind. That's good.

Let's general-purpose that API up. I leave this in your ingenious Eurozone hands. May we all survive the brewing pandemic hellstorm and live to tell the tale over thick and pungent tiergarten lager.











Fix patch:
diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD
index abf332de..83f72fb6 100644
--- a/CONTRIBUTING.MD
+++ b/CONTRIBUTING.MD
@@ -1,14 +1,15 @@
 Hi! Thank you for your interest in contributing to justuse :)
 
-Besides tackling [good first issues](https://github.com/amogorkon/justuse/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22), there is plenty to do - writing unit tests and try to run them on different platforms and network configurations,
-write documentation or come up with questions and ideas to keep us on our toes.
+Besides tackling [good first issues](https://github.com/amogorkon/justuse/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22), there is plenty other stuff to do - like writing unit tests and documentation, finding bugs and report them or coming up with new ideas and questions to keep us on our toes.
 
-For questions and general discussion, you can use [github discussions](https://github.com/amogorkon/justuse/discussions), for documentation [the github wiki](https://github.com/amogorkon/justuse/wiki).
+For general ideas and questions, you can use [github discussions](https://github.com/amogorkon/justuse/discussions), for documentation please refer to our [wiki](https://github.com/amogorkon/justuse/wiki) and for discussions regarding code, and always feel free to join our [slack channel](https://app.slack.com/client/T0297SNMCHY).
 
 If you want to contribute code, be aware that we (at least I do) run black/brunette to enforce proper code formatting. 
-You can use any indentation style (2,3,4 spaces, tabs..) you like on your side, but don't complain if they get mangled to 4-space indentation.
+You can use any indentation style (2,3,4 spaces, tabs..) you like on your side, but don't complain if they get mangled to 4-space indentation. Please follow PEP8 as far as is reasonable.
 
 Please make sure that your code is properly commented - always ask yourself "would future self immediately understand why I wrote it like this in a year from now?" if the answer is no, add a comment.
 And please try to refrain from code golfing. We all know the temptation, but.. :)
 
+To streamline commit messages, automatically managing issues and making them generally more useful for debugging, please follow [How to write good commit Messages](https://chiamakaikeanyi.dev/how-to-write-good-git-commit-messages/)
+
 Well, that's it. Welcome aboard!
diff --git a/README.md b/README.md
index 765f6faf..90996893 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,14 @@
 
 # Just use() python the way you want!
 
+```mermaid
+graph TD;
+    A[just]-->B["use()"];
+    B --> C["Path()"]
+    B --> D["URL()"]
+    B --> E[Packages]
+```
+
 ## Installation
 To install, enter `python -m pip install justuse` in a commandline, then you can `import use` in your code and simply use() stuff. Check for examples below and our [Showcase](https://github.com/amogorkon/justuse/blob/unstable/docs/Showcase.ipynb)!
 
@@ -22,10 +30,10 @@ To install, enter `python -m pip install justuse` in a commandline, then you can
 - [x] inline version-checking
 - [x] safely import code from an online URL - towards an unhackable infrastructure ("Rather die than get corrupted!")
 - [x] initial module globals - a straight forward solution to diamond/cyclic imports
-- [x] decorate all specified callables (functions, methods, classes, ..) on import via pattern matching, aspect-orientation made easy
+- [x] decorate *everything* callable recursively via pattern matching, aspect-orientation made easy (except closures, those are *hard*)
 - [x] return a given default if an exception happened during an import, simplifying optional dependencies
 - [x] safe hot auto-reloading of function-only local modules - a REPL-like dev experience with files in jupyter and regular python interpreters
-- [x] safely auto-install version-tagged pure python packages from PyPI (packages with C-extensions like numpy don't work yet)
+- [x] safely auto-install version-tagged pure python packages from PyPI
 - [x] have multiple versions of the same package installed and loaded in the same program without conflicts
 - [x] auto-install packages with C-extensions and other precompiled stuff
 - [x] no-hassle inline auto-installation of (almost) all conda packages
@@ -34,8 +42,9 @@ To install, enter `python -m pip install justuse` in a commandline, then you can
 - [ ] all justuse-code is compiled to a single, standalone .py file - just drop it into your own code without installation
 - [ ] provide a visual representation of the internal dependency graph
 - [ ] module-level variable guards aka "module-properties"
-- [ ] isolation of packages via subprocess/subinterpreter for clean un-/reloading
+- [ ] isolation of software components with arbitrary sub-interpreters (python 2.7, ironpython..) inspired by [jupyter messaging](https://jupyter-client.readthedocs.io/en/latest/messaging.html)
 - [ ] slot-based plugin architecture (to ensure reproducable testability of plugin-combinations)
+- [ ] optional on-site compilation of fully annotated python code via cython
 - [ ] document everything!
 - [ ] test everything!
 
diff --git a/docs/Showcase.ipynb b/docs/Showcase.ipynb
index 7714a5d0..d72a9e53 100644
--- a/docs/Showcase.ipynb
+++ b/docs/Showcase.ipynb
@@ -20,11 +20,11 @@
    "execution_count": 1,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T10:19:30.432457Z",
-     "iopub.status.busy": "2021-11-26T10:19:30.431362Z",
-     "iopub.status.idle": "2021-11-26T10:19:36.135747Z",
-     "shell.execute_reply": "2021-11-26T10:19:36.134369Z",
-     "shell.execute_reply.started": "2021-11-26T10:19:30.432197Z"
+     "iopub.execute_input": "2021-11-26T14:28:17.214084Z",
+     "iopub.status.busy": "2021-11-26T14:28:17.213181Z",
+     "iopub.status.idle": "2021-11-26T14:28:23.791904Z",
+     "shell.execute_reply": "2021-11-26T14:28:23.789995Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:17.213932Z"
     }
    },
    "outputs": [
@@ -33,29 +33,29 @@
      "output_type": "stream",
      "text": [
       "Collecting justuse\n",
-      "  Using cached justuse-0.6.3-py3-none-any.whl (113 kB)\n",
-      "Requirement already satisfied: wheel>=0.36.2 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (0.37.0)\n",
-      "Requirement already satisfied: icontract>=2.5.4 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (2.5.5)\n",
-      "Requirement already satisfied: furl>=2.1.2 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (2.1.2)\n",
-      "Requirement already satisfied: requests>=2.24.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (2.24.0)\n",
-      "Requirement already satisfied: packaging==21.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (21.0)\n",
+      "  Downloading justuse-0.6.5-py3-none-any.whl (113 kB)\n",
+      "Requirement already satisfied: hypothesis>=6.23.1 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (6.24.2)\n",
       "Requirement already satisfied: pip in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (21.3.1)\n",
+      "Requirement already satisfied: furl>=2.1.2 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (2.1.2)\n",
       "Requirement already satisfied: typeguard>=2.12.1 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (2.13.0)\n",
-      "Requirement already satisfied: hypothesis>=6.23.1 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (6.24.2)\n",
+      "Requirement already satisfied: requests>=2.24.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (2.24.0)\n",
       "Requirement already satisfied: pydantic>=1.8.2 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (1.8.2)\n",
+      "Requirement already satisfied: icontract>=2.5.4 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (2.5.5)\n",
+      "Requirement already satisfied: wheel>=0.36.2 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (0.37.0)\n",
+      "Requirement already satisfied: packaging==21.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from justuse) (21.0)\n",
       "Requirement already satisfied: pyparsing>=2.0.2 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from packaging==21.0->justuse) (2.4.7)\n",
-      "Requirement already satisfied: six>=1.8.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from furl>=2.1.2->justuse) (1.15.0)\n",
       "Requirement already satisfied: orderedmultidict>=1.0.1 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from furl>=2.1.2->justuse) (1.0.1)\n",
-      "Requirement already satisfied: attrs>=19.2.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from hypothesis>=6.23.1->justuse) (20.3.0)\n",
+      "Requirement already satisfied: six>=1.8.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from furl>=2.1.2->justuse) (1.15.0)\n",
       "Requirement already satisfied: sortedcontainers<3.0.0,>=2.1.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from hypothesis>=6.23.1->justuse) (2.2.2)\n",
+      "Requirement already satisfied: attrs>=19.2.0 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from hypothesis>=6.23.1->justuse) (20.3.0)\n",
       "Requirement already satisfied: typing-extensions in /home/thorsten/anaconda3/lib/python3.8/site-packages (from icontract>=2.5.4->justuse) (3.7.4.3)\n",
       "Requirement already satisfied: asttokens<3,>=2 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from icontract>=2.5.4->justuse) (2.0.5)\n",
-      "Requirement already satisfied: certifi>=2017.4.17 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from requests>=2.24.0->justuse) (2020.6.20)\n",
       "Requirement already satisfied: chardet<4,>=3.0.2 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from requests>=2.24.0->justuse) (3.0.4)\n",
       "Requirement already satisfied: idna<3,>=2.5 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from requests>=2.24.0->justuse) (2.10)\n",
       "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from requests>=2.24.0->justuse) (1.25.11)\n",
+      "Requirement already satisfied: certifi>=2017.4.17 in /home/thorsten/anaconda3/lib/python3.8/site-packages (from requests>=2.24.0->justuse) (2020.6.20)\n",
       "Installing collected packages: justuse\n",
-      "Successfully installed justuse-0.6.3\n"
+      "Successfully installed justuse-0.6.5\n"
      ]
     }
    ],
@@ -69,11 +69,11 @@
    "execution_count": 2,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T13:39:18.453064Z",
-     "iopub.status.busy": "2021-11-26T13:39:18.452487Z",
-     "iopub.status.idle": "2021-11-26T13:39:20.105892Z",
-     "shell.execute_reply": "2021-11-26T13:39:20.103936Z",
-     "shell.execute_reply.started": "2021-11-26T13:39:18.452996Z"
+     "iopub.execute_input": "2021-11-26T14:28:23.795704Z",
+     "iopub.status.busy": "2021-11-26T14:28:23.794952Z",
+     "iopub.status.idle": "2021-11-26T14:28:24.937678Z",
+     "shell.execute_reply": "2021-11-26T14:28:24.935805Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:23.795650Z"
     }
    },
    "outputs": [],
@@ -86,18 +86,18 @@
    "execution_count": 3,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T13:39:20.109594Z",
-     "iopub.status.busy": "2021-11-26T13:39:20.109061Z",
-     "iopub.status.idle": "2021-11-26T13:39:20.137419Z",
-     "shell.execute_reply": "2021-11-26T13:39:20.134529Z",
-     "shell.execute_reply.started": "2021-11-26T13:39:20.109556Z"
+     "iopub.execute_input": "2021-11-26T14:28:24.942835Z",
+     "iopub.status.busy": "2021-11-26T14:28:24.941470Z",
+     "iopub.status.idle": "2021-11-26T14:28:24.962554Z",
+     "shell.execute_reply": "2021-11-26T14:28:24.960562Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:24.942744Z"
     }
    },
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "'0.6.4'"
+       "'0.6.5'"
       ]
      },
      "execution_count": 3,
@@ -136,11 +136,11 @@
    "execution_count": 4,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T10:19:39.944272Z",
-     "iopub.status.busy": "2021-11-26T10:19:39.943791Z",
-     "iopub.status.idle": "2021-11-26T10:19:40.180706Z",
-     "shell.execute_reply": "2021-11-26T10:19:40.179176Z",
-     "shell.execute_reply.started": "2021-11-26T10:19:39.944221Z"
+     "iopub.execute_input": "2021-11-26T14:28:30.345876Z",
+     "iopub.status.busy": "2021-11-26T14:28:30.345049Z",
+     "iopub.status.idle": "2021-11-26T14:28:30.624941Z",
+     "shell.execute_reply": "2021-11-26T14:28:30.622636Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:30.345826Z"
     }
    },
    "outputs": [
@@ -159,6 +159,13 @@
     "use(\"math\").cos(23)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Here we loaded math but without adding it to the global namespace, so if you only need one thing from a module or package, you don't have to pollute your namespace."
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {
@@ -186,11 +193,11 @@
    "execution_count": 5,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T10:19:41.621756Z",
-     "iopub.status.busy": "2021-11-26T10:19:41.621295Z",
-     "iopub.status.idle": "2021-11-26T10:19:41.632392Z",
-     "shell.execute_reply": "2021-11-26T10:19:41.629772Z",
-     "shell.execute_reply.started": "2021-11-26T10:19:41.621700Z"
+     "iopub.execute_input": "2021-11-26T14:28:31.076004Z",
+     "iopub.status.busy": "2021-11-26T14:28:31.074659Z",
+     "iopub.status.idle": "2021-11-26T14:28:31.086203Z",
+     "shell.execute_reply": "2021-11-26T14:28:31.084605Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:31.075911Z"
     }
    },
    "outputs": [],
@@ -233,11 +240,11 @@
    "execution_count": 6,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T10:19:42.954204Z",
-     "iopub.status.busy": "2021-11-26T10:19:42.952925Z",
-     "iopub.status.idle": "2021-11-26T10:19:42.969966Z",
-     "shell.execute_reply": "2021-11-26T10:19:42.968232Z",
-     "shell.execute_reply.started": "2021-11-26T10:19:42.954072Z"
+     "iopub.execute_input": "2021-11-26T14:28:31.773381Z",
+     "iopub.status.busy": "2021-11-26T14:28:31.772906Z",
+     "iopub.status.idle": "2021-11-26T14:28:31.785105Z",
+     "shell.execute_reply": "2021-11-26T14:28:31.782482Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:31.773344Z"
     }
    },
    "outputs": [
@@ -272,14 +279,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 28,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T10:19:43.675152Z",
-     "iopub.status.busy": "2021-11-26T10:19:43.673877Z",
-     "iopub.status.idle": "2021-11-26T10:19:43.904582Z",
-     "shell.execute_reply": "2021-11-26T10:19:43.903542Z",
-     "shell.execute_reply.started": "2021-11-26T10:19:43.675077Z"
+     "iopub.execute_input": "2021-11-26T16:36:50.866880Z",
+     "iopub.status.busy": "2021-11-26T16:36:50.866253Z",
+     "iopub.status.idle": "2021-11-26T16:36:50.887007Z",
+     "shell.execute_reply": "2021-11-26T16:36:50.885353Z",
+     "shell.execute_reply.started": "2021-11-26T16:36:50.866839Z"
     }
    },
    "outputs": [
@@ -296,6 +303,17 @@
     "    print(\"I know Kung Fu!\")"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "It is also possible to lazily chain use() calls with defaults like\n",
+    "\n",
+    "> math = use(\"numpy\", default=None) or use(\"math\")\n",
+    "\n",
+    "which will evaluate from left to right and return whichever call returns something truthy (like a viable module) first."
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -309,11 +327,11 @@
    "execution_count": 8,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T10:19:44.654174Z",
-     "iopub.status.busy": "2021-11-26T10:19:44.652292Z",
-     "iopub.status.idle": "2021-11-26T10:19:44.916176Z",
-     "shell.execute_reply": "2021-11-26T10:19:44.914688Z",
-     "shell.execute_reply.started": "2021-11-26T10:19:44.654026Z"
+     "iopub.execute_input": "2021-11-26T14:28:33.328521Z",
+     "iopub.status.busy": "2021-11-26T14:28:33.326226Z",
+     "iopub.status.idle": "2021-11-26T14:28:33.684551Z",
+     "shell.execute_reply": "2021-11-26T14:28:33.683126Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:33.328360Z"
     }
    },
    "outputs": [
@@ -335,11 +353,11 @@
    "execution_count": 9,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T10:19:45.456753Z",
-     "iopub.status.busy": "2021-11-26T10:19:45.455022Z",
-     "iopub.status.idle": "2021-11-26T10:19:45.466976Z",
-     "shell.execute_reply": "2021-11-26T10:19:45.465365Z",
-     "shell.execute_reply.started": "2021-11-26T10:19:45.456607Z"
+     "iopub.execute_input": "2021-11-26T14:28:34.043159Z",
+     "iopub.status.busy": "2021-11-26T14:28:34.041723Z",
+     "iopub.status.idle": "2021-11-26T14:28:34.052605Z",
+     "shell.execute_reply": "2021-11-26T14:28:34.050906Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:34.043103Z"
     }
    },
    "outputs": [
@@ -374,14 +392,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 10,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T13:39:32.135604Z",
-     "iopub.status.busy": "2021-11-26T13:39:32.135012Z",
-     "iopub.status.idle": "2021-11-26T13:39:32.420161Z",
-     "shell.execute_reply": "2021-11-26T13:39:32.415970Z",
-     "shell.execute_reply.started": "2021-11-26T13:39:32.135540Z"
+     "iopub.execute_input": "2021-11-26T14:28:36.977619Z",
+     "iopub.status.busy": "2021-11-26T14:28:36.976500Z",
+     "iopub.status.idle": "2021-11-26T14:28:37.061350Z",
+     "shell.execute_reply": "2021-11-26T14:28:37.058455Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:36.977439Z"
     }
    },
    "outputs": [
@@ -392,37 +410,37 @@
      "traceback": [
       "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
       "\u001b[0;31mImportError\u001b[0m                               Traceback (most recent call last)",
-      "\u001b[0;32m<ipython-input-4-916309d36fa0>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mspam\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0muse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"pygame\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/main.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    101\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    102\u001b[0m         \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__condition\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 103\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__implementation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    104\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    105\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/tools.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kw)\u001b[0m\n\u001b[1;32m     40\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     41\u001b[0m             \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 42\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mdispatcher\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdispatch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     43\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     44\u001b[0m     \u001b[0mwrapper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdispatcher\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/main.py\u001b[0m in \u001b[0;36m_use_str\u001b[0;34m(self, name, version, hash_algo, hashes, default, modes)\u001b[0m\n\u001b[1;32m    706\u001b[0m         \"\"\"\n\u001b[1;32m    707\u001b[0m         \u001b[0mpackage_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodule_name\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_parse_name\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 708\u001b[0;31m         return self._use_package(\n\u001b[0m\u001b[1;32m    709\u001b[0m             \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    710\u001b[0m             \u001b[0mpackage_name\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpackage_name\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/main.py\u001b[0m in \u001b[0;36m_use_package\u001b[0;34m(self, name, package_name, module_name, version, hashes, modes, default, hash_algo, user_msg)\u001b[0m\n\u001b[1;32m    777\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    778\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 779\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0m_fail_or_default\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    780\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    781\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mModuleType\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/pimp.py\u001b[0m in \u001b[0;36m_fail_or_default\u001b[0;34m(exception, default)\u001b[0m\n\u001b[1;32m    972\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mdefault\u001b[0m  \u001b[0;31m# TODO: write test for default\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    973\u001b[0m     \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 974\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mexception\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[0;32m<ipython-input-10-916309d36fa0>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mspam\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0muse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"pygame\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/main.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    101\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    102\u001b[0m         \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__condition\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 103\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__implementation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    104\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    105\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/tools.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kw)\u001b[0m\n\u001b[1;32m     40\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     41\u001b[0m             \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 42\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mdispatcher\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdispatch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     43\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     44\u001b[0m     \u001b[0mwrapper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdispatcher\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/main.py\u001b[0m in \u001b[0;36m_use_str\u001b[0;34m(self, name, version, hash_algo, hashes, default, modes)\u001b[0m\n\u001b[1;32m    706\u001b[0m         \"\"\"\n\u001b[1;32m    707\u001b[0m         \u001b[0mpackage_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodule_name\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_parse_name\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 708\u001b[0;31m         return self._use_package(\n\u001b[0m\u001b[1;32m    709\u001b[0m             \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    710\u001b[0m             \u001b[0mpackage_name\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpackage_name\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/main.py\u001b[0m in \u001b[0;36m_use_package\u001b[0;34m(self, name, package_name, module_name, version, hashes, modes, default, hash_algo, user_msg)\u001b[0m\n\u001b[1;32m    777\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    778\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 779\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0m_fail_or_default\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    780\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    781\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mModuleType\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/pimp.py\u001b[0m in \u001b[0;36m_fail_or_default\u001b[0;34m(exception, default)\u001b[0m\n\u001b[1;32m    972\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mdefault\u001b[0m  \u001b[0;31m# TODO: write test for default\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    973\u001b[0m     \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 974\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mexception\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
       "\u001b[0;31mImportError\u001b[0m: No pkg installed named pygame and auto-installation not requested. Aborting."
      ]
     }
    ],
    "source": [
-    "spam = use(\"pygame\")"
+    "pg = use(\"pygame\")"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Well, bummer! Why can't we have spam? We need spam, give us spam!"
+    "Well, bummer! We want to play with pygame, let's have pygame!"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 11,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T13:39:36.308324Z",
-     "iopub.status.busy": "2021-11-26T13:39:36.307198Z",
-     "iopub.status.idle": "2021-11-26T13:39:41.630626Z",
-     "shell.execute_reply": "2021-11-26T13:39:41.626123Z",
-     "shell.execute_reply.started": "2021-11-26T13:39:36.308214Z"
+     "iopub.execute_input": "2021-11-26T14:28:40.029837Z",
+     "iopub.status.busy": "2021-11-26T14:28:40.029119Z",
+     "iopub.status.idle": "2021-11-26T14:28:43.387312Z",
+     "shell.execute_reply": "2021-11-26T14:28:43.372274Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:40.029769Z"
     }
    },
    "outputs": [
@@ -433,25 +451,25 @@
      "traceback": [
       "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
       "\u001b[0;31mRuntimeWarning\u001b[0m                            Traceback (most recent call last)",
-      "\u001b[0;32m<ipython-input-5-939f1366e66f>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mspam\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0muse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"pygame\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodes\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0muse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_install\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/main.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    101\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    102\u001b[0m         \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__condition\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 103\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__implementation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    104\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    105\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/tools.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kw)\u001b[0m\n\u001b[1;32m     40\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     41\u001b[0m             \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 42\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mdispatcher\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdispatch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     43\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     44\u001b[0m     \u001b[0mwrapper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdispatcher\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/main.py\u001b[0m in \u001b[0;36m_use_str\u001b[0;34m(self, name, version, hash_algo, hashes, default, modes)\u001b[0m\n\u001b[1;32m    706\u001b[0m         \"\"\"\n\u001b[1;32m    707\u001b[0m         \u001b[0mpackage_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodule_name\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_parse_name\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 708\u001b[0;31m         return self._use_package(\n\u001b[0m\u001b[1;32m    709\u001b[0m             \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    710\u001b[0m             \u001b[0mpackage_name\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpackage_name\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/main.py\u001b[0m in \u001b[0;36m_use_package\u001b[0;34m(self, name, package_name, module_name, version, hashes, modes, default, hash_algo, user_msg)\u001b[0m\n\u001b[1;32m    777\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    778\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 779\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0m_fail_or_default\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    780\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    781\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mModuleType\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/media/sf_Dropbox/code/justuse/src/use/pimp.py\u001b[0m in \u001b[0;36m_fail_or_default\u001b[0;34m(exception, default)\u001b[0m\n\u001b[1;32m    972\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mdefault\u001b[0m  \u001b[0;31m# TODO: write test for default\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    973\u001b[0m     \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 974\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mexception\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[0;32m<ipython-input-11-939f1366e66f>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mspam\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0muse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"pygame\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodes\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0muse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_install\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/main.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    101\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    102\u001b[0m         \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__condition\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 103\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__implementation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    104\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    105\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/tools.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kw)\u001b[0m\n\u001b[1;32m     40\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     41\u001b[0m             \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 42\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mdispatcher\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdispatch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     43\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     44\u001b[0m     \u001b[0mwrapper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdispatcher\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregister\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/main.py\u001b[0m in \u001b[0;36m_use_str\u001b[0;34m(self, name, version, hash_algo, hashes, default, modes)\u001b[0m\n\u001b[1;32m    706\u001b[0m         \"\"\"\n\u001b[1;32m    707\u001b[0m         \u001b[0mpackage_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodule_name\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_parse_name\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 708\u001b[0;31m         return self._use_package(\n\u001b[0m\u001b[1;32m    709\u001b[0m             \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    710\u001b[0m             \u001b[0mpackage_name\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpackage_name\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/main.py\u001b[0m in \u001b[0;36m_use_package\u001b[0;34m(self, name, package_name, module_name, version, hashes, modes, default, hash_algo, user_msg)\u001b[0m\n\u001b[1;32m    777\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    778\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 779\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0m_fail_or_default\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    780\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    781\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mModuleType\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/use/pimp.py\u001b[0m in \u001b[0;36m_fail_or_default\u001b[0;34m(exception, default)\u001b[0m\n\u001b[1;32m    972\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mdefault\u001b[0m  \u001b[0;31m# TODO: write test for default\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    973\u001b[0m     \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 974\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mexception\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
       "\u001b[0;31mRuntimeWarning\u001b[0m: Please specify version and hash for auto-installation of 'pygame'.\nA webbrowser will open to the Snyk Advisor to check whether the package is vulnerable.\nIf you want to auto-install the latest version:\nuse(\"pygame\", version=\"2.1.0\", hashes={'J娼䥺瑹鍖谫癦䯅恢搸烃诺嬝矫暠鸤嘀邦'}, modes=use.auto_install)"
      ]
     }
    ],
    "source": [
-    "spam = use(\"pygame\", modes=use.auto_install)"
+    "pg = use(\"pygame\", modes=use.auto_install)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Now we're getting somewhere! Hmm.. it says \"To get some valuable insight on the health of this package, please check out https://snyk.io/advisor/python/spam\" - see for yourself!"
+    "Now we're getting somewhere! Hmm.. it says \"To get some valuable insight on the health of this package, please check out https://snyk.io/advisor/python/pygame - see for yourself!"
    ]
   },
   {
@@ -478,14 +496,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 12,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T13:40:52.811186Z",
-     "iopub.status.busy": "2021-11-26T13:40:52.810465Z",
-     "iopub.status.idle": "2021-11-26T13:40:53.220588Z",
-     "shell.execute_reply": "2021-11-26T13:40:53.219620Z",
-     "shell.execute_reply.started": "2021-11-26T13:40:52.811148Z"
+     "iopub.execute_input": "2021-11-26T14:28:54.873909Z",
+     "iopub.status.busy": "2021-11-26T14:28:54.873391Z",
+     "iopub.status.idle": "2021-11-26T14:28:55.077317Z",
+     "shell.execute_reply": "2021-11-26T14:28:55.076587Z",
+     "shell.execute_reply.started": "2021-11-26T14:28:54.873871Z"
     }
    },
    "outputs": [
@@ -503,7 +521,7 @@
        "<module 'pygame' from '/home/thorsten/.justuse-python/venv/pygame/2.1.0/lib/python3.8/site-packages/pygame/__init__.py'>"
       ]
      },
-     "execution_count": 6,
+     "execution_count": 12,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -524,7 +542,7 @@
     }
    },
    "source": [
-    "Wow, did we just download, install and load the spam package without leaving our own sweet code?? Yes, we did!"
+    "Wow, did we just download, install and load the pygame package without leaving our own sweet code?? Yes, we did!"
    ]
   },
   {
@@ -1008,19 +1026,68 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## Aspects of code"
+    "## Aspects and decorators\n",
+    "If you have code like\n",
+    "\n",
+    "```\n",
+    "def foo(x):\n",
+    "    return x ** 2\n",
+    "```\n",
+    "There are two ways to modify the behaviour of the code. Let's say you want to know which arguments were passed in and you want to print those arguments, so you can either add the print statement inside like\n",
+    "```\n",
+    "def foo(x):\n",
+    "    print(x)\n",
+    "    return x ** 2\n",
+    "```\n",
+    "or you can wrap the function with another function which gets called instead like\n",
+    "\n",
+    "```\n",
+    "def decorator(x):\n",
+    "    print(x)\n",
+    "    return foo(x)\n",
+    "```\n",
+    "which you can also write as a function that takes a function as argument - a \"higher order function\" if you will - like\n",
+    "```\n",
+    "def decorator(func):\n",
+    "   def wrapper(*args, **kwargs):\n",
+    "       print(*args, **kwargs)\n",
+    "       return func(*args, **kwargs)\n",
+    "   return wrapper\n",
+    "foo = wrap(foo)\n",
+    "foo(x)\n",
+    "```\n",
+    "which looks a bit convoluted, so there is some syntactic sugar in python to make it look nicer:\n",
+    "```\n",
+    "@decorator\n",
+    "def foo(x):\n",
+    "    return x ** 2\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "There is nothing wrong with this approach, it's a plain and simple decorator, obvious reading the code. The only big problem is that single decorator is nice and simple but adding more quickly gets messy and complicated, not just because it gets hard to read and reason about but also because writing correct decorators is not as plain and simple as it may look like. For example, the *decorator* function above is bad because it will shadow the signature and docstring of the function it is wrapping. The second big problem is that it is quite messy to add decorators manually to code with lots of functions or methods. It can be not only a lot to write but also add visual noise, making it harder to read code (the opposite of what it was supposed to do!) while making it harder to keep [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). The third big problem, no wait, amongst the chief problems of manually adding decorators is that it can be quite painful or impossible to deal with callables (functions, methods but also classes can be callable!) that originate in code you can't control, for example code that isn't even written in python but in C (like numpy)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Our solution: use() adds functionality to the modules you request and gives you an easy way to decorate *everything* in a single line:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 15,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T00:49:34.315607Z",
-     "iopub.status.busy": "2021-11-26T00:49:34.315109Z",
-     "iopub.status.idle": "2021-11-26T00:49:34.685751Z",
-     "shell.execute_reply": "2021-11-26T00:49:34.684621Z",
-     "shell.execute_reply.started": "2021-11-26T00:49:34.315568Z"
+     "iopub.execute_input": "2021-11-26T15:29:01.700128Z",
+     "iopub.status.busy": "2021-11-26T15:29:01.699648Z",
+     "iopub.status.idle": "2021-11-26T15:29:01.713190Z",
+     "shell.execute_reply": "2021-11-26T15:29:01.712051Z",
+     "shell.execute_reply.started": "2021-11-26T15:29:01.700089Z"
     }
    },
    "outputs": [],
@@ -1039,14 +1106,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 16,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T00:49:35.162242Z",
-     "iopub.status.busy": "2021-11-26T00:49:35.161750Z",
-     "iopub.status.idle": "2021-11-26T00:49:35.271176Z",
-     "shell.execute_reply": "2021-11-26T00:49:35.269893Z",
-     "shell.execute_reply.started": "2021-11-26T00:49:35.162196Z"
+     "iopub.execute_input": "2021-11-26T15:29:02.214297Z",
+     "iopub.status.busy": "2021-11-26T15:29:02.213336Z",
+     "iopub.status.idle": "2021-11-26T15:29:02.270864Z",
+     "shell.execute_reply": "2021-11-26T15:29:02.267503Z",
+     "shell.execute_reply.started": "2021-11-26T15:29:02.214179Z"
     }
    },
    "outputs": [
@@ -1056,7 +1123,7 @@
        "<module 'numpy' from '/home/thorsten/anaconda3/lib/python3.8/site-packages/numpy/__init__.py'>"
       ]
      },
-     "execution_count": 4,
+     "execution_count": 16,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1065,16 +1132,163 @@
     "np @ (use.isfunction, \"\", decorator)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2021-11-26T15:28:48.065021Z",
+     "iopub.status.busy": "2021-11-26T15:28:48.064485Z",
+     "iopub.status.idle": "2021-11-26T15:28:48.074691Z",
+     "shell.execute_reply": "2021-11-26T15:28:48.072916Z",
+     "shell.execute_reply.started": "2021-11-26T15:28:48.064974Z"
+    }
+   },
+   "source": [
+    "Let's have a look at our log file to see what just happened:"
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 17,
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2021-11-26T15:29:04.122158Z",
+     "iopub.status.busy": "2021-11-26T15:29:04.121562Z",
+     "iopub.status.idle": "2021-11-26T15:29:04.148995Z",
+     "shell.execute_reply": "2021-11-26T15:29:04.145725Z",
+     "shell.execute_reply.started": "2021-11-26T15:29:04.122118Z"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "16:29:02,248 use.pimp DEBUG Applied decorator to numpy::take_along_axis [function]\n",
+      "16:29:02,248 use.pimp DEBUG Applied decorator to numpy::put_along_axis [function]\n",
+      "16:29:02,248 use.pimp DEBUG Applied decorator to numpy::broadcast_to [function]\n",
+      "16:29:02,248 use.pimp DEBUG Applied decorator to numpy::broadcast_arrays [function]\n",
+      "16:29:02,248 use.pimp DEBUG Applied decorator to numpy::diag [function]\n",
+      "16:29:02,248 use.pimp DEBUG Applied decorator to numpy::diagflat [function]\n",
+      "16:29:02,248 use.pimp DEBUG Applied decorator to numpy::eye [function]\n",
+      "16:29:02,248 use.pimp DEBUG Applied decorator to numpy::fliplr [function]\n",
+      "16:29:02,249 use.pimp DEBUG Applied decorator to numpy::flipud [function]\n",
+      "16:29:02,249 use.pimp DEBUG Applied decorator to numpy::tri [function]\n",
+      "16:29:02,249 use.pimp DEBUG Applied decorator to numpy::triu [function]\n",
+      "16:29:02,249 use.pimp DEBUG Applied decorator to numpy::tril [function]\n",
+      "16:29:02,250 use.pimp DEBUG Applied decorator to numpy::vander [function]\n",
+      "16:29:02,250 use.pimp DEBUG Applied decorator to numpy::histogram2d [function]\n",
+      "16:29:02,250 use.pimp DEBUG Applied decorator to numpy::mask_indices [function]\n",
+      "16:29:02,250 use.pimp DEBUG Applied decorator to numpy::tril_indices [function]\n",
+      "16:29:02,251 use.pimp DEBUG Applied decorator to numpy::tril_indices_from [function]\n",
+      "16:29:02,251 use.pimp DEBUG Applied decorator to numpy::triu_indices [function]\n",
+      "16:29:02,251 use.pimp DEBUG Applied decorator to numpy::triu_indices_from [function]\n",
+      "16:29:02,251 use.pimp DEBUG Applied decorator to numpy::fix [function]\n",
+      "16:29:02,251 use.pimp DEBUG Applied decorator to numpy::isneginf [function]\n",
+      "16:29:02,251 use.pimp DEBUG Applied decorator to numpy::isposinf [function]\n",
+      "16:29:02,251 use.pimp DEBUG Applied decorator to numpy::pad [function]\n",
+      "16:29:02,251 use.pimp DEBUG Applied decorator to numpy::poly [function]\n",
+      "16:29:02,252 use.pimp DEBUG Applied decorator to numpy::roots [function]\n",
+      "16:29:02,252 use.pimp DEBUG Applied decorator to numpy::polyint [function]\n",
+      "16:29:02,252 use.pimp DEBUG Applied decorator to numpy::polyder [function]\n",
+      "16:29:02,252 use.pimp DEBUG Applied decorator to numpy::polyadd [function]\n",
+      "16:29:02,252 use.pimp DEBUG Applied decorator to numpy::polysub [function]\n",
+      "16:29:02,252 use.pimp DEBUG Applied decorator to numpy::polymul [function]\n",
+      "16:29:02,252 use.pimp DEBUG Applied decorator to numpy::polydiv [function]\n",
+      "16:29:02,252 use.pimp DEBUG Applied decorator to numpy::polyval [function]\n",
+      "16:29:02,253 use.pimp DEBUG Applied decorator to numpy::polyfit [function]\n",
+      "16:29:02,253 use.pimp DEBUG Applied decorator to numpy::issubclass_ [function]\n",
+      "16:29:02,253 use.pimp DEBUG Applied decorator to numpy::issubsctype [function]\n",
+      "16:29:02,253 use.pimp DEBUG Applied decorator to numpy.lib.utils::deprecate [function]\n",
+      "16:29:02,253 use.pimp DEBUG Applied decorator to numpy.lib.utils::<lambda> [function]\n",
+      "16:29:02,253 use.pimp DEBUG Applied decorator to numpy.lib.utils::get_include [function]\n",
+      "16:29:02,253 use.pimp DEBUG Applied decorator to numpy::info [function]\n",
+      "16:29:02,253 use.pimp DEBUG Applied decorator to numpy::source [function]\n",
+      "16:29:02,254 use.pimp DEBUG Applied decorator to numpy.lib.utils::who [function]\n",
+      "16:29:02,254 use.pimp DEBUG Applied decorator to numpy::lookfor [function]\n",
+      "16:29:02,254 use.pimp DEBUG Applied decorator to numpy.lib.utils::byte_bounds [function]\n",
+      "16:29:02,254 use.pimp DEBUG Applied decorator to numpy.lib.utils::safe_eval [function]\n",
+      "16:29:02,254 use.pimp DEBUG Applied decorator to numpy::ediff1d [function]\n",
+      "16:29:02,254 use.pimp DEBUG Applied decorator to numpy::intersect1d [function]\n",
+      "16:29:02,254 use.pimp DEBUG Applied decorator to numpy::setxor1d [function]\n",
+      "16:29:02,255 use.pimp DEBUG Applied decorator to numpy::union1d [function]\n",
+      "16:29:02,255 use.pimp DEBUG Applied decorator to numpy::setdiff1d [function]\n",
+      "16:29:02,255 use.pimp DEBUG Applied decorator to numpy::unique [function]\n",
+      "16:29:02,255 use.pimp DEBUG Applied decorator to numpy::in1d [function]\n",
+      "16:29:02,255 use.pimp DEBUG Applied decorator to numpy::isin [function]\n",
+      "16:29:02,255 use.pimp DEBUG Applied decorator to numpy::savetxt [function]\n",
+      "16:29:02,256 use.pimp DEBUG Applied decorator to numpy::loadtxt [function]\n",
+      "16:29:02,256 use.pimp DEBUG Applied decorator to numpy::genfromtxt [function]\n",
+      "16:29:02,256 use.pimp DEBUG Applied decorator to numpy.lib.npyio::ndfromtxt [function]\n",
+      "16:29:02,256 use.pimp DEBUG Applied decorator to numpy.lib.npyio::mafromtxt [function]\n",
+      "16:29:02,256 use.pimp DEBUG Applied decorator to numpy.lib.npyio::recfromtxt [function]\n",
+      "16:29:02,256 use.pimp DEBUG Applied decorator to numpy.lib.npyio::recfromcsv [function]\n",
+      "16:29:02,256 use.pimp DEBUG Applied decorator to numpy::load [function]\n",
+      "16:29:02,256 use.pimp DEBUG Applied decorator to numpy::loads [function]\n",
+      "16:29:02,257 use.pimp DEBUG Applied decorator to numpy::save [function]\n",
+      "16:29:02,257 use.pimp DEBUG Applied decorator to numpy::savez [function]\n",
+      "16:29:02,257 use.pimp DEBUG Applied decorator to numpy::savez_compressed [function]\n",
+      "16:29:02,257 use.pimp DEBUG Applied decorator to numpy::packbits [function]\n",
+      "16:29:02,257 use.pimp DEBUG Applied decorator to numpy::unpackbits [function]\n",
+      "16:29:02,257 use.pimp DEBUG Applied decorator to numpy::fromregex [function]\n",
+      "16:29:02,257 use.pimp DEBUG Applied decorator to numpy::fv [function]\n",
+      "16:29:02,257 use.pimp DEBUG Applied decorator to numpy::pmt [function]\n",
+      "16:29:02,258 use.pimp DEBUG Applied decorator to numpy::nper [function]\n",
+      "16:29:02,258 use.pimp DEBUG Applied decorator to numpy::ipmt [function]\n",
+      "16:29:02,258 use.pimp DEBUG Applied decorator to numpy::ppmt [function]\n",
+      "16:29:02,258 use.pimp DEBUG Applied decorator to numpy::pv [function]\n",
+      "16:29:02,258 use.pimp DEBUG Applied decorator to numpy::rate [function]\n",
+      "16:29:02,258 use.pimp DEBUG Applied decorator to numpy::irr [function]\n",
+      "16:29:02,258 use.pimp DEBUG Applied decorator to numpy::npv [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::mirr [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nansum [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nanmax [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nanmin [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nanargmax [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nanargmin [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nanmean [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nanmedian [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nanpercentile [function]\n",
+      "16:29:02,259 use.pimp DEBUG Applied decorator to numpy::nanvar [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::nanstd [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::nanprod [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::nancumsum [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::nancumprod [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::nanquantile [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::histogram [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::histogramdd [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::histogram_bin_edges [function]\n",
+      "16:29:02,260 use.pimp DEBUG Applied decorator to numpy::bmat [function]\n",
+      "16:29:02,261 use.pimp DEBUG Applied decorator to numpy::asmatrix [function]\n",
+      "16:29:02,261 use.pimp DEBUG Applied decorator to numpy::asmatrix [function]\n",
+      "16:29:02,261 use.pimp DEBUG Applied decorator to numpy::round_ [function]\n",
+      "16:29:02,261 use.pimp DEBUG Applied decorator to numpy::amax [function]\n",
+      "16:29:02,261 use.pimp DEBUG Applied decorator to numpy::amin [function]\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%bash\n",
+    "tail -100 /home/thorsten/.justuse-python/logs/usage.log"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We wrapped *all* functions in the numpy package with a decorator that lets us directly observe what happens when we call something without the need for an external debugger!"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T00:49:35.936224Z",
-     "iopub.status.busy": "2021-11-26T00:49:35.935761Z",
-     "iopub.status.idle": "2021-11-26T00:49:35.969388Z",
-     "shell.execute_reply": "2021-11-26T00:49:35.968352Z",
-     "shell.execute_reply.started": "2021-11-26T00:49:35.936188Z"
+     "iopub.execute_input": "2021-11-26T15:30:22.116852Z",
+     "iopub.status.busy": "2021-11-26T15:30:22.116398Z",
+     "iopub.status.idle": "2021-11-26T15:30:22.129248Z",
+     "shell.execute_reply": "2021-11-26T15:30:22.127185Z",
+     "shell.execute_reply.started": "2021-11-26T15:30:22.116815Z"
     }
    },
    "outputs": [
@@ -1082,8 +1296,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "<function amax at 0x7fb360739670> (array(...),) {}\n",
-      "<function amin at 0x7fb360739820> (array(...),) {}\n"
+      "<function amax at 0x7fd13028baf0> (array(...),) {}\n",
+      "<function amin at 0x7fd13028bca0> (array(...),) {}\n"
      ]
     },
     {
@@ -1092,7 +1306,7 @@
        "array([1, 2, 3])"
       ]
      },
-     "execution_count": 5,
+     "execution_count": 18,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1101,135 +1315,85 @@
     "np.array([1,2,3])"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "If you aren't in a situation where you need to debug, log or control access (the classical applications of AOP) you might still want to use it in python - for type-checking/testing!\n",
+    "For example you can apply our favorite dynamic type checker library beartype quite easily like so:"
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 20,
    "metadata": {
     "execution": {
-     "iopub.execute_input": "2021-11-26T00:54:41.616148Z",
-     "iopub.status.busy": "2021-11-26T00:54:41.615469Z",
-     "iopub.status.idle": "2021-11-26T00:54:41.637722Z",
-     "shell.execute_reply": "2021-11-26T00:54:41.636172Z",
-     "shell.execute_reply.started": "2021-11-26T00:54:41.616071Z"
+     "iopub.execute_input": "2021-11-26T15:34:55.974746Z",
+     "iopub.status.busy": "2021-11-26T15:34:55.974205Z",
+     "iopub.status.idle": "2021-11-26T15:34:56.018229Z",
+     "shell.execute_reply": "2021-11-26T15:34:56.016461Z",
+     "shell.execute_reply.started": "2021-11-26T15:34:55.974704Z"
     }
    },
    "outputs": [
     {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "01:49:35,243 use.pimp DEBUG Applied decorator to savez\n",
-      "01:49:35,243 use.pimp DEBUG isfunction((<function savez_compressed at 0x7fb360195550>,), {})\n",
-      "01:49:35,243 use.pimp DEBUG Applied decorator to savez_compressed\n",
-      "01:49:35,243 use.pimp DEBUG isfunction((<function packbits at 0x7fb36076b0d0>,), {})\n",
-      "01:49:35,243 use.pimp DEBUG Applied decorator to packbits\n",
-      "01:49:35,243 use.pimp DEBUG isfunction((<function unpackbits at 0x7fb36076b1f0>,), {})\n",
-      "01:49:35,243 use.pimp DEBUG Applied decorator to unpackbits\n",
-      "01:49:35,244 use.pimp DEBUG isfunction((<function fromregex at 0x7fb360195940>,), {})\n",
-      "01:49:35,244 use.pimp DEBUG Applied decorator to fromregex\n",
-      "01:49:35,244 use.pimp DEBUG isfunction((<class 'numpy.DataSource'>,), {})\n",
-      "01:49:35,244 use.pimp DEBUG isfunction((<function fv at 0x7fb360195f70>,), {})\n",
-      "01:49:35,244 use.pimp DEBUG Applied decorator to fv\n",
-      "01:49:35,244 use.pimp DEBUG isfunction((<function pmt at 0x7fb3601a0160>,), {})\n",
-      "01:49:35,244 use.pimp DEBUG Applied decorator to pmt\n",
-      "01:49:35,244 use.pimp DEBUG isfunction((<function nper at 0x7fb3601a0310>,), {})\n",
-      "01:49:35,244 use.pimp DEBUG Applied decorator to nper\n",
-      "01:49:35,244 use.pimp DEBUG isfunction((<function ipmt at 0x7fb3601a04c0>,), {})\n",
-      "01:49:35,244 use.pimp DEBUG Applied decorator to ipmt\n",
-      "01:49:35,244 use.pimp DEBUG isfunction((<function ppmt at 0x7fb3601a0700>,), {})\n",
-      "01:49:35,245 use.pimp DEBUG Applied decorator to ppmt\n",
-      "01:49:35,245 use.pimp DEBUG isfunction((<function pv at 0x7fb3601a08b0>,), {})\n",
-      "01:49:35,245 use.pimp DEBUG Applied decorator to pv\n",
-      "01:49:35,245 use.pimp DEBUG isfunction((<function rate at 0x7fb3601a0af0>,), {})\n",
-      "01:49:35,245 use.pimp DEBUG Applied decorator to rate\n",
-      "01:49:35,245 use.pimp DEBUG isfunction((<function irr at 0x7fb3601a0ca0>,), {})\n",
-      "01:49:35,245 use.pimp DEBUG Applied decorator to irr\n",
-      "01:49:35,245 use.pimp DEBUG isfunction((<function npv at 0x7fb3601a0e50>,), {})\n",
-      "01:49:35,245 use.pimp DEBUG Applied decorator to npv\n",
-      "01:49:35,245 use.pimp DEBUG isfunction((<function mirr at 0x7fb360123040>,), {})\n",
-      "01:49:35,246 use.pimp DEBUG Applied decorator to mirr\n",
-      "01:49:35,246 use.pimp DEBUG isfunction((<function nansum at 0x7fb3601bcaf0>,), {})\n",
-      "01:49:35,246 use.pimp DEBUG Applied decorator to nansum\n",
-      "01:49:35,246 use.pimp DEBUG isfunction((<function nanmax at 0x7fb3601bc5e0>,), {})\n",
-      "01:49:35,246 use.pimp DEBUG Applied decorator to nanmax\n",
-      "01:49:35,246 use.pimp DEBUG isfunction((<function nanmin at 0x7fb3601bc430>,), {})\n",
-      "01:49:35,246 use.pimp DEBUG Applied decorator to nanmin\n",
-      "01:49:35,246 use.pimp DEBUG isfunction((<function nanargmax at 0x7fb3601bc940>,), {})\n",
-      "01:49:35,246 use.pimp DEBUG Applied decorator to nanargmax\n",
-      "01:49:35,246 use.pimp DEBUG isfunction((<function nanargmin at 0x7fb3601bc790>,), {})\n",
-      "01:49:35,246 use.pimp DEBUG Applied decorator to nanargmin\n",
-      "01:49:35,247 use.pimp DEBUG isfunction((<function nanmean at 0x7fb3601bf1f0>,), {})\n",
-      "01:49:35,247 use.pimp DEBUG Applied decorator to nanmean\n",
-      "01:49:35,247 use.pimp DEBUG isfunction((<function nanmedian at 0x7fb3601bf550>,), {})\n",
-      "01:49:35,247 use.pimp DEBUG Applied decorator to nanmedian\n",
-      "01:49:35,247 use.pimp DEBUG isfunction((<function nanpercentile at 0x7fb3601bf700>,), {})\n",
-      "01:49:35,247 use.pimp DEBUG Applied decorator to nanpercentile\n",
-      "01:49:35,247 use.pimp DEBUG isfunction((<function nanvar at 0x7fb3601bfc10>,), {})\n",
-      "01:49:35,247 use.pimp DEBUG Applied decorator to nanvar\n",
-      "01:49:35,247 use.pimp DEBUG isfunction((<function nanstd at 0x7fb3601bfdc0>,), {})\n",
-      "01:49:35,247 use.pimp DEBUG Applied decorator to nanstd\n",
-      "01:49:35,247 use.pimp DEBUG isfunction((<function nanprod at 0x7fb3601bcca0>,), {})\n",
-      "01:49:35,247 use.pimp DEBUG Applied decorator to nanprod\n",
-      "01:49:35,248 use.pimp DEBUG isfunction((<function nancumsum at 0x7fb3601bce50>,), {})\n",
-      "01:49:35,248 use.pimp DEBUG Applied decorator to nancumsum\n",
-      "01:49:35,248 use.pimp DEBUG isfunction((<function nancumprod at 0x7fb3601bf040>,), {})\n",
-      "01:49:35,248 use.pimp DEBUG Applied decorator to nancumprod\n",
-      "01:49:35,248 use.pimp DEBUG isfunction((<function nanquantile at 0x7fb3601bf8b0>,), {})\n",
-      "01:49:35,248 use.pimp DEBUG Applied decorator to nanquantile\n",
-      "01:49:35,248 use.pimp DEBUG isfunction((<function histogram at 0x7fb360421700>,), {})\n",
-      "01:49:35,248 use.pimp DEBUG Applied decorator to histogram\n",
-      "01:49:35,248 use.pimp DEBUG isfunction((<function histogramdd at 0x7fb3604218b0>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG Applied decorator to histogramdd\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<function histogram_bin_edges at 0x7fb360421550>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG Applied decorator to histogram_bin_edges\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<module 'numpy.fft' from '/home/thorsten/anaconda3/lib/python3.8/site-packages/numpy/fft/__init__.py'>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<module 'numpy.polynomial' from '/home/thorsten/anaconda3/lib/python3.8/site-packages/numpy/polynomial/__init__.py'>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<module 'numpy.random' from '/home/thorsten/anaconda3/lib/python3.8/site-packages/numpy/random/__init__.py'>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<module 'numpy.ctypeslib' from '/home/thorsten/anaconda3/lib/python3.8/site-packages/numpy/ctypeslib.py'>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<module 'numpy.ma' from '/home/thorsten/anaconda3/lib/python3.8/site-packages/numpy/ma/__init__.py'>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<module 'numpy.matrixlib' from '/home/thorsten/anaconda3/lib/python3.8/site-packages/numpy/matrixlib/__init__.py'>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<class 'numpy.matrix'>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG isfunction((<function bmat at 0x7fb360406ee0>,), {})\n",
-      "01:49:35,249 use.pimp DEBUG Applied decorator to bmat\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<function asmatrix at 0x7fb360402b80>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG Applied decorator to asmatrix\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<function asmatrix at 0x7fb360402b80>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG Applied decorator to asmatrix\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<class 'bool'>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<class 'int'>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<class 'float'>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<class 'complex'>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<class 'object'>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<class 'str'>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<function round_ at 0x7fb36073d8b0>,), {})\n",
-      "01:49:35,250 use.pimp DEBUG Applied decorator to round_\n",
-      "01:49:35,250 use.pimp DEBUG isfunction((<ufunc 'absolute'>,), {})\n",
-      "01:49:35,251 use.pimp DEBUG isfunction((<function amax at 0x7fb360739670>,), {})\n",
-      "01:49:35,251 use.pimp DEBUG Applied decorator to amax\n",
-      "01:49:35,251 use.pimp DEBUG isfunction((<function amin at 0x7fb360739820>,), {})\n",
-      "01:49:35,251 use.pimp DEBUG Applied decorator to amin\n",
-      "01:49:35,251 use.pimp DEBUG isfunction((<numpy._pytesttester.PytestTester object at 0x7fb36011b190>,), {})\n",
-      "01:49:35,251 use.pimp DEBUG isfunction((<module 'os' from '/home/thorsten/anaconda3/lib/python3.8/os.py'>,), {})\n",
-      "01:49:35,251 use.pimp DEBUG isfunction((1,), {})\n",
-      "01:53:41,655 parso.python.diff DEBUG diff parser start\n",
-      "01:53:41,655 parso.python.diff DEBUG line_lengths old: 1; new: 1\n",
-      "01:53:41,655 parso.python.diff DEBUG -> code[replace] old[1:1] new[1:1]\n",
-      "01:53:41,656 parso.python.diff DEBUG parse_part from 1 to 1 (to 0 in part parser)\n",
-      "01:53:41,656 parso.python.diff DEBUG diff parser end\n",
-      "01:53:41,818 parso.cache DEBUG pickle loaded: /home/thorsten/anaconda3/lib/python3.8/site-packages/jedi/third_party/typeshed/stdlib/2and3/builtins.pyi\n",
-      "01:53:42,0 parso.cache DEBUG pickle loaded: /home/thorsten/anaconda3/lib/python3.8/site-packages/jedi/third_party/typeshed/stdlib/3/sys.pyi\n"
-     ]
+     "data": {
+      "text/plain": [
+       "<module 'numpy' from '/home/thorsten/anaconda3/lib/python3.8/site-packages/numpy/__init__.py'>"
+      ]
+     },
+     "execution_count": 20,
+     "metadata": {},
+     "output_type": "execute_result"
     }
    ],
    "source": [
-    "%%bash\n",
-    "tail -100 /home/thorsten/.justuse-python/usage.log"
+    "from beartype import beartype\n",
+    "\n",
+    "np @ (use.isfunction, \"\", beartype)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2021-11-26T15:35:16.483099Z",
+     "iopub.status.busy": "2021-11-26T15:35:16.482573Z",
+     "iopub.status.idle": "2021-11-26T15:35:16.507905Z",
+     "shell.execute_reply": "2021-11-26T15:35:16.505878Z",
+     "shell.execute_reply.started": "2021-11-26T15:35:16.483062Z"
+    }
+   },
+   "source": [
+    "Now all functions in numpy are beartype-checked! You can do the same with your own code, as long as you use() your modules."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2021-11-26T15:32:34.252907Z",
+     "iopub.status.busy": "2021-11-26T15:32:34.252454Z",
+     "iopub.status.idle": "2021-11-26T15:32:34.270157Z",
+     "shell.execute_reply": "2021-11-26T15:32:34.266549Z",
+     "shell.execute_reply.started": "2021-11-26T15:32:34.252870Z"
+    }
+   },
+   "source": [
+    "If you want to learn more about aspect-oriented programming, check out https://en.wikipedia.org/wiki/Aspect-oriented_programming "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Even though applying decorators this way usually is super simple and nice, one word of caution: in python it sometimes is not clear at all *what* something is. If you do `foo(x)` foo could be a pure function, an object with a `__call__` method or you might be calling the `__init__` method of a class. While it is nice for the user to be oblivious about those details in general, in this case it can be quite confusing and frustrating because each case needs to be handled differently and using a check like isfunction might be catching the wrong things. So, be aware of these potential pitfalls before applying decorators blindly without checking what actually got hit."
    ]
   }
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": "Python 3 (ipykernel)",
    "language": "python",
    "name": "python3"
   },
diff --git a/docs/codeflow.md b/docs/codeflow.md
new file mode 100644
index 00000000..d848102e
--- /dev/null
+++ b/docs/codeflow.md
@@ -0,0 +1,56 @@
+This is an overflow of how justuse works. This should give you an idea where to look for things. Don't rely on this document.
+
+## Initialization
+
+```mermaid
+graph LR;
+    A(__init__.py)-->B(main.py);
+    B(main.py)-->C(instance of class Use replaces module use in __init__.py);
+```
+## Modes of operation
+
+This is defined on the class Use via singledispatch on the type of argument
+```mermaid
+graph LR;
+    A(use) --> B[\Path - module is a local file\]
+    A(use) --> C[\URL - module is an online resource\]
+    A(use) --> D[\string - module is part of a package, which may or may not be installed\]
+```
+
+## use package
+While using local or online resources as modules is very straight forward, using modules as part of packages is not.
+To use a module from a package with auto-installation, you need to think of the name to be used as two-parted. The first part is how you would pip-install it, the second is how you would import the module. You can specify the name in three ways:
+
+    # package name that you would pip install = "py.foo"
+    # module name that you would import = "foo.bar"
+    
+    use("py.foo/foo.bar")
+    use(("py.foo", "foo.bar"))
+    use(package_name="py.foo", module_name="foo.bar")
+
+However you call it, it all gets normalized into `name` (the single string representation), `package_name` for installation and `module_name` for import.
+
+```mermaid
+graph TD
+    A(main.Use._use_package) --> B[\normalization of all call-data into a dictionary of keyword args\]
+    B --> C["buffet table" of functions, for dispatch on whichever combination of conditions]
+    C --> D[\each function gets called with the same kwargs, picking the kwargs it needs, ignoring the rest\]
+```
+
+### auto-installation
+Inline-installation of packages is one of the most interesting and complex features of justuse.
+
+With version and hash properly defined and auto-installation requested, the flow of action is as following:
+```mermaid
+graph TD
+    A(main.Use._use_package) --> B[\pimp._auto_install\]
+    B --> C{found in registry?}
+    C -- yes --> D{zip?}
+    D -- yes --> E[\try to import it directly via zipimport\]
+    D -- no --> F[\try to install it using pip\]
+    C -- no --> G[\download the artifact\]
+    G --> D
+    F --> H[import it via importlib]
+    H --> I
+    E --> I[return mod]
+```
diff --git a/docs/database schema.md b/docs/database schema.md
new file mode 100644
index 00000000..e46f75c0
--- /dev/null
+++ b/docs/database schema.md
@@ -0,0 +1,41 @@
+This is the schema of the registry database for reference.
+
+* Packages have a name and version, but are released as platform-dependent (or independent, as pure-python) distributions.
+* Each distribution corresponds to a single downloadable and installable file, called an artifact.
+* Each artifact has hashes that are generated using certain algorithms, which map to a very large number (no matter how this number is represented otherwise - as hexdigest or JACK or something else).
+* Once a distribution has been installed the artifact could be removed (since everything is now unpacked, compiled etc), but it also can be kept for further P2P sharing.
+* The distribution is installed in a venv, isolated. 
+
+```mermaid
+
+  erDiagram
+
+    artifacts {
+    INTEGER id
+    INTEGER distribution_id
+    TEXT import_relpath
+    TEXT artifact_path
+    TEXT module_path
+    }
+
+    distributions {
+      INTEGER id
+      TEXT name
+      TEXT version
+      TEXT installation_path
+      INTEGER date_of_installation
+      INTEGER number_of_uses
+      INTEGER date_of_last_use
+      INTEGER pure_python_package
+    }
+
+    hashes {
+      TEXT algo
+      INTEGER value
+      INTEGER artifact_id
+    }
+
+    hashes ||--o{ artifacts : "foreign key"
+    artifacts ||--o{ distributions : "foreign key"
+
+```
diff --git a/extra/web-message-mockup.pptx b/extra/web-message-mockup.pptx
new file mode 100644
index 00000000..883302b0
Binary files /dev/null and b/extra/web-message-mockup.pptx differ
diff --git a/requirements.txt b/requirements.txt
index 84c7f819..c9c0e1ae 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,4 +9,5 @@ furl ~= 2.1.2  #!
 wheel ~= 0.36.2  #!
 icontract ~= 2.5.4  #-
 beartype >= 0.8.1  #-
-hypothesis ~= 6.23.1  #-
\ No newline at end of file
+hypothesis ~= 6.23.1  #-
+jinja2 ~= 3.0.3
\ No newline at end of file
diff --git a/src/use/__init__.py b/src/use/__init__.py
index f279f449..3c07747d 100755
--- a/src/use/__init__.py
+++ b/src/use/__init__.py
@@ -1,7 +1,6 @@
 """
 This is where the story begins. Welcome to JustUse!
 Only imports and project-global constants are defined here. 
-    
 """
 
 
@@ -11,9 +10,12 @@
     import gc
     import types
     import typing
+    from abc import ABCMeta
+    from collections.abc import Callable
+    from types import CellType
     from typing import _GenericAlias as GenericAlias
 
-    for t in (list, dict, set, tuple, frozenset):
+    for t in (list, dict, set, tuple, frozenset, ABCMeta, Callable, CellType):
         r = gc.get_referents(t.__dict__)[0]
         r.update(
             {
@@ -21,51 +23,70 @@
             }
         )
 
-
 import hashlib
 import os
+import tempfile
 from collections import namedtuple
+from datetime import datetime
 from enum import Enum, IntEnum
-from inspect import isfunction, ismethod  # for aspectizing, DO NOT REMOVE
-from logging import DEBUG, INFO, NOTSET, basicConfig, getLogger, root
+from logging import DEBUG, basicConfig, getLogger
 from pathlib import Path
+from typing import NamedTuple
+from uuid import uuid4
 from warnings import catch_warnings, filterwarnings, simplefilter
 
+import toml
 from beartype import beartype
 from beartype.roar import BeartypeDecorHintPep585DeprecationWarning
 
-# Coerce all PEP 585 deprecation warnings into fatal exceptions.
-catch_warnings()
-filterwarnings("ignore", category=DeprecationWarning, module="pip")
-filterwarnings("error", category=BeartypeDecorHintPep585DeprecationWarning)
+from use.pydantics import Configuration
 
+sessionID = uuid4()
 
-home = Path(os.getenv("JUSTUSE_HOME", str(Path.home() / ".justuse-python"))).absolute()
+home = Path(os.getenv("JUSTUSE_HOME", str(Path.home() / ".justuse-python")))
 
 try:
     home.mkdir(mode=0o755, parents=True, exist_ok=True)
 except PermissionError:
     # this should fix the permission issues on android #80
-
     home = tempfile.mkdtemp(prefix="justuse_")
-(home / "packages").mkdir(mode=0o755, parents=True, exist_ok=True)
+
+(home / "config.toml").touch(mode=0o755, exist_ok=True)
+config_dict = toml.load(home / "config.toml")
+
+config = Configuration(**config_dict)
+
+config.logs.mkdir(mode=0o755, parents=True, exist_ok=True)
+config.packages.mkdir(mode=0o755, parents=True, exist_ok=True)
+
 for file in (
-    "config.toml",
-    "config_defaults.toml",
-    "usage.log",
-    "registry.db",
-    "user_registry.toml",
+    config.logs / "usage.log",
+    config.registry,
 ):
-    (home / file).touch(mode=0o755, exist_ok=True)
-
+    file.touch(mode=0o755, exist_ok=True)
+
+
+def fraction_of_day(now: datetime = None) -> float:
+    if now is None:
+        now = datetime.utcnow()
+    return round(
+        (
+            now.hour / 24
+            + now.minute / (24 * 60)
+            + now.second / (24 * 60 * 60)
+            + now.microsecond / (24 * 60 * 60 * 1000 * 1000)
+        )
+        * 1000,
+        6,
+    )
 
-config = {"version_warning": True, "debugging": False, "use_db": True, "testing": False}
 
 basicConfig(
-    filename=home / "usage.log",
+    filename=config.logs / "usage.log",
     filemode="a",
-    format="%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s",
-    datefmt="%H:%M:%S",
+    format="%(asctime)s %(levelname)s %(name)s %(message)s",
+    datefmt=f"%Y%m%d {fraction_of_day()}",
+    # datefmt="%Y-%m-%d %H:%M:%S",
     level=DEBUG,
 )
 
@@ -74,89 +95,98 @@
 # current use __version__ variable **AS A STRING LITERAL** from
 # this file. If you do anything except updating the version,
 # please check that setup.py can still be executed.
-__version__ = "0.6.5"  # IMPORTANT; Must leave exactly as-is for setup
+__version__ = "0.6.6"
+# for tests
+__version__ = os.getenv("USE_VERSION", __version__)
 __name__ = "use"
 __package__ = "use"
 
+log = getLogger(__name__)
+log.info(f"### SESSION START {datetime.now().strftime('%Y/%m/%d %H:%M:%S')} {sessionID} ###")
+
+import use.logutil
+
+
+class JustuseIssue:
+    pass
+
 
-class VersionWarning(Warning):
+class NirvanaWarning(Warning, JustuseIssue):
     pass
 
 
-class NotReloadableWarning(Warning):
+class VersionWarning(Warning, JustuseIssue):
     pass
 
 
-class NoValidationWarning(Warning):
+class NotReloadableWarning(Warning, JustuseIssue):
     pass
 
 
-class AmbiguityWarning(Warning):
+class NoValidationWarning(Warning, JustuseIssue):
     pass
 
 
-class UnexpectedHash(ImportError):
+class AmbiguityWarning(Warning, JustuseIssue):
     pass
 
 
-class AutoInstallationError(ImportError):
+class UnexpectedHash(ImportError, JustuseIssue):
     pass
 
 
-from use.hash_alphabet import *
+class InstallationError(ImportError, JustuseIssue):
+    pass
+
+
+# Coerce all PEP 585 deprecation warnings into fatal exceptions.
+with catch_warnings():
+    from beartype.roar import BeartypeDecorHintPep585DeprecationWarning
 
-ModInUse = namedtuple("ModInUse", "name mod path spec frame")
-NoneType = type(None)
+    filterwarnings("ignore", category=BeartypeDecorHintPep585DeprecationWarning, module="beartype")
 
+    from use.hash_alphabet import *
 
-class Hash(Enum):
-    sha256 = hashlib.sha256
+    ModInUse = namedtuple("ModInUse", "name mod path spec frame")
 
+    class Hash(Enum):
+        sha256 = hashlib.sha256
+        blake = hashlib.blake2s
 
-class Modes(IntEnum):
-    auto_install = 2 ** 0
-    fatal_exceptions = 2 ** 1
-    reloading = 2 ** 2
-    no_public_installation = 2 ** 4
-    fastfail = 2 ** 5
+    class Modes(IntEnum):
+        auto_install = 2**0
+        fatal_exceptions = 2**1
+        reloading = 2**2
+        no_public_installation = 2**3
+        fastfail = 2**4
+        recklessness = 2**5
+        no_browser = 2**6
+        no_cleanup = 2**7
 
 
+from use.aspectizing import *
+from use.aspectizing import apply_aspect
 from use.buffet_old import buffet_table
 from use.main import *
 from use.messages import *
+
 ### NEEDED FOR TESTS!! ###
 from use.pimp import *
-from use.pimp import (_get_package_data, _get_version, _is_version_satisfied,
-                      _parse_name, get_supported)
-from use.pypi_model import *
+from use.pimp import _get_data_from_pypi, _get_version, _is_version_satisfied, _parse_name, get_supported
+from use.pydantics import *
 from use.tools import *
 
-use = Use()
-use.__dict__.update({k: v for k, v in globals().items()})  # to avoid recursion-confusion
-use = ProxyModule(use)
-
-
-def decorator_log_calling_function_and_args(func, *args):
-    """
-    Decorator to log the calling function and its arguments.
-
-    Args:
-        func (function): The function to decorate.
-        *args: The arguments to pass to the function.
-
-    Returns:
-        function: The decorated function.
-    """
+for member in Modes:
+    setattr(Use, member.name, member.value)
 
-    def wrapper(*args, **kwargs):
-        log.debug(f"{func.__name__}({args}, {kwargs})")
-        return func(*args, **kwargs)
-
-    return wrapper
+use = Use()
 
+use.__dict__.update(dict(globals()))
+use = ProxyModule(use)
 
-use @ (isfunction, "", beartype)
-use @ (isfunction, "", decorator_log_calling_function_and_args)
+with catch_warnings():
+    filterwarnings("ignore", category=BeartypeDecorHintPep585DeprecationWarning, module="beartype")
+    # apply_aspect(use, beartype, check=isbeartypeable, pattern="")
 
 if not test_version:
     sys.modules["use"] = use
diff --git a/src/use/aspectizing.py b/src/use/aspectizing.py
new file mode 100644
index 00000000..e6d26219
--- /dev/null
+++ b/src/use/aspectizing.py
@@ -0,0 +1,267 @@
+import builtins
+import inspect
+import re
+import sys
+from collections import namedtuple
+from collections.abc import Callable
+from enum import Enum
+from functools import wraps
+from importlib.util import spec_from_loader
+from logging import getLogger
+from pathlib import Path
+from time import perf_counter_ns
+from types import ModuleType
+from typing import Any, DefaultDict, Deque, Optional
+from warnings import catch_warnings, filterwarnings
+
+from beartype import beartype
+from beartype.roar import BeartypeDecorHintPep585DeprecationWarning
+from icontract import require
+
+log = getLogger(__name__)
+
+from use import config
+from use.messages import _web_aspectized, _web_aspectized_dry_run, _web_tinny_profiler
+
+# TODO: use an extra WeakKeyDict as watchdog for object deletions and trigger cleanup in these here
+_applied_decorators: DefaultDict[tuple[object, str], Deque[Callable]] = DefaultDict(Deque)
+"{qualname: [callable]} - to see which decorators are applied, in which order"
+_aspectized_functions: DefaultDict[tuple[object, str], Deque[Callable]] = DefaultDict(Deque)
+"{qualname: [callable]} - the actually decorated functions to undo aspectizing"
+
+
+def show_aspects():
+    """Open a browser to properly display all the things that have been aspectized thus far."""
+    print("decorators:", _applied_decorators)
+    print("functions:", _aspectized_functions)
+    # _web_aspectized(_applied_decorators, _aspectized_functions)
+
+
+def is_callable(thing):
+    try:
+        object.__getattribute__(thing, "__call__")
+        return True
+    except AttributeError:
+        return False
+
+
+HIT = namedtuple("Hit", "qualname name type success exception dunder")
+
+
+@beartype
+def apply_aspect(
+    thing: Any,
+    decorator: Callable,
+    /,
+    *,
+    check: Callable = is_callable,
+    pattern: str = "",
+    module_name: Optional[str] = None,
+    regex: Optional[re.Pattern] = None,
+    aspectize_dunders: bool = False,
+    recursive: bool = True,
+    dry_run: bool = False,
+    visited: Optional[set[int]] = None,
+    excluded_names: Optional[set[str]] = None,
+    excluded_types: Optional[set[type]] = None,
+    hits: Optional[list[HIT]] = None,
+    last: bool = True,  # and first
+    qualname_lst: Optional[list] = None,
+) -> Any:
+    """Apply the aspect as a side-effect, no copy is created."""
+    name = getattr(thing, "__name__", str(thing))
+    if not qualname_lst:
+        qualname_lst = []
+
+    # to prevent recursion into the depths of hell and beyond
+    if visited is None:
+        visited = {id(obj) for obj in vars(object).values()}
+        visited.add(id(type))
+
+    if id(thing) in visited:
+        return
+
+    qualname_lst.append(name)
+
+    # initial call
+    if module_name is None:
+        try:
+            module_name = inspect.getmodule(thing).__name__
+        except AttributeError:
+            module_name = "<unknown>"
+    else:  # recursive call - let's stick within the module boundary
+        try:
+            thing_module_name = inspect.getmodule(thing).__name__
+        except AttributeError:
+            thing_module_name = "<unknown>"
+        if thing_module_name != module_name:
+            return
+
+    if hits is None:
+        hits = []
+
+    if excluded_names is None:
+        excluded_names = set()
+    if excluded_types is None:
+        excluded_types = set()
+
+    # We compile once so in subsequent recursive calls, we have it already.
+    if not regex:
+        regex = re.compile(pattern, re.DOTALL)
+
+    for name in dir(thing):
+        qualname = "" if len(qualname_lst) < 3 else "..." + ".".join(qualname_lst[-3:]) + "." + name
+        obj = getattr(thing, name, None)
+        qualname = getattr(obj, "__qualname__", qualname)
+
+        if obj is None:
+            continue
+        # Time to get serious!
+
+        if recursive and type(obj) == type:
+            apply_aspect(
+                obj,
+                decorator,
+                check=check,
+                pattern=pattern,
+                module_name=module_name,
+                regex=regex,
+                aspectize_dunders=aspectize_dunders,
+                excluded_names=excluded_names,
+                excluded_types=excluded_types,
+                recursive=True,
+                visited=visited,
+                dry_run=dry_run,
+                hits=hits,
+                last=False,
+                qualname_lst=qualname_lst,
+            )
+
+        if id(obj) in visited:
+            continue
+
+        if (
+            qualname in excluded_names
+            or type(obj) in excluded_types
+            or not check(obj)
+            or not regex.match(name)
+        ):
+            continue
+
+        msg = ""
+        success = True
+
+        try:
+            wrapped = _wrap(thing=thing, obj=obj, decorator=decorator, name=name)
+            if dry_run:
+                _unwrap(thing=thing, name=name)
+        except BaseException as exc:
+            wrapped = obj
+            success = False
+            msg = str(exc)
+        hits.append(
+            HIT(qualname, name, type(wrapped), success, msg, name.startswith("__") and name.endswith("__"))
+        )
+        visited.add(id(wrapped))
+
+    if last and dry_run:
+        if config.no_browser:
+            print("Tried to do a dry run and display the results, but no_browser is set in config.")
+        else:
+            print("Please check your browser to select options and filters for aspects.")
+            _web_aspectized_dry_run(
+                decorator=decorator,
+                pattern=pattern,
+                check=check,
+                hits=hits,
+                module_name=module_name,
+            )
+    return thing
+
+
+@beartype
+def _wrap(*, thing: Any, obj: Any, decorator: Callable, name: str) -> Any:
+    wrapped = decorator(obj)
+    _applied_decorators[(thing, name)].append(decorator)
+    _aspectized_functions[(thing, name)].append(obj)
+
+    # This will fail with TypeError on built-in/extension types.
+    # We handle exceptions outside, let's not betray ourselves.
+    setattr(thing, name, wrapped)
+    return wrapped
+
+
+@beartype
+def _unwrap(*, thing: Any, name: str):
+    try:
+        original = _aspectized_functions[(thing, name)].pop()
+    except IndexError:
+        del _aspectized_functions[(thing, name)]
+        original = getattr(thing, name)
+
+    try:
+        _applied_decorators[(thing, name)].pop()
+    except IndexError:
+        del _applied_decorators[(thing, name)]
+    setattr(thing, name, original)
+
+    return original
+
+
+def woody_logger(func: Callable) -> Callable:
+    """
+    Decorator to log/track/debug calls and results.
+
+    Args:
+        func (function): The function to decorate.
+    Returns:
+        function: The decorated callable.
+    """
+    qualname = getattr(func, "__qualname__", None) or getattr(func, "__name__", None) or str(func)
+    module = getattr(func, "__module__", None) or getattr(func.__class__, "__module__", None)
+    if module:
+        module += "."
+
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        print(f"{args} {kwargs} -> {module}{qualname}")
+        before = perf_counter_ns()
+        res = func(*args, **kwargs)
+        after = perf_counter_ns()
+        # log.debug(
+        #     f"{func.__module__}::{getattr(func, '__qualname__', func.__name__)}({args}, {kwargs}) -> {res} {type(res)}"
+        # )
+        print(
+            f"-> {module}{qualname} (in {after - before} ns ({round((after - before) / 10**9, 5)} sec) -> {res} {type(res)}"
+        )
+        return res
+
+    return wrapper
+
+
+_timings: dict[int, Deque[int]] = DefaultDict(lambda: Deque(maxlen=10000))
+
+
+def tinny_profiler(func: callable) -> callable:
+    """
+    Decorator to log/track/debug calls and results.
+
+    Args:
+        func (function): The function to decorate.
+    Returns:
+        function: The decorated callable.
+    """
+
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        before = perf_counter_ns()
+        res = func(*args, **kwargs)
+        after = perf_counter_ns()
+        _timings[func].append(after - before)
+        return res
+
+    return wrapper
+
+
+def show_profiling():
+    _web_tinny_profiler(_timings)
diff --git a/src/use/buffet.py b/src/use/buffet.py
index 10fba013..e3eb8698 100644
--- a/src/use/buffet.py
+++ b/src/use/buffet.py
@@ -12,6 +12,13 @@
 
 def buffet_table(case, kwargs):
     match case: # type: ignore
+#            +-------------------------- version specified?
+#            |  +----------------------- hash specified?
+#            |  |  +-------------------- already publicly available?
+#            |  |  |  +----------------- auto-install requested?
+#            |  |  |  |
+#            v  v  v  v      
+#            |  |  |  | 
         case _, _, 0, 0: return ImportError(Message.cant_import(**kwargs))
         case 0, _, 1, 0: return _import_public_no_install(**kwargs)
         case 1, _, 1, 0: return _import_public_no_install(**kwargs) >> _ensure_version(**kwargs)
diff --git a/src/use/buffet_old.py b/src/use/buffet_old.py
index 4722d741..8ea0e848 100644
--- a/src/use/buffet_old.py
+++ b/src/use/buffet_old.py
@@ -8,7 +8,7 @@
 log = getLogger(__name__)
 
 from use import pimp
-from use.messages import Message
+from use.messages import UserMessage as Message
 
 # the buffet of the past (<3.10)
 
@@ -35,8 +35,6 @@ def buffet_table(case, kwargs):
             func=lambda: pimp._ensure_version(pimp._import_public_no_install(**kwargs), **kwargs), **kwargs
         ),
     }[case]
-    log.info("case_func = '%s' %s", case_func.__qualname__, case_func)
-    log.info("kwargs = %s", repr(kwargs))
     result = case_func()
     log.info("result = %s", repr(result))
     return result
diff --git a/src/use/hash_alphabet.py b/src/use/hash_alphabet.py
index f5e1256a..54837dea 100755
--- a/src/use/hash_alphabet.py
+++ b/src/use/hash_alphabet.py
@@ -2,12 +2,12 @@
 The JACK hash alphabet. 
 For details see https://pypi.org/project/jackhash
 """
-
+import use
 
 # korean characters: https://github.com/arcsecw/wubi/blob/master/wubi/cw.py
 # chinese characters: https://github.com/tsroten/zhon/blob/develop/zhon/cedict/all.py
 
-__version__ = "4"
+__version__ = "5"
 
 chinese_characters = """○
 、
@@ -40614,8 +40614,7 @@
 﨤
 﨧
 﨨
-﨩
-""".split(
+﨩""".split(
     "\n"
 )
 
@@ -40627,8 +40626,16 @@
 
 # emojis only cause interoperability issues
 # japanese alphabet would only add 100 individual chars, not worth the trouble (and GPL is prohibitive)
-alphabet = sorted(list(set(ascii_characters + chinese_characters + korean_characters)))
-reverse_alphabet = {c: i for i, c in enumerate(alphabet)}
+class AlphabetAccess:
+    # to avoid printing the full alphabet to stderr whenever there's a problem
+    global ascii_characters
+    global chinese_characters
+    global korean_characters
+    alphabet = sorted(list(set(ascii_characters + chinese_characters + korean_characters)))
+    reverse_alphabet = {c: i for i, c in enumerate(alphabet)}
+    del ascii_characters
+    del chinese_characters
+    del korean_characters
 
 
 def represent_num_as_base(num, base):
@@ -40644,14 +40651,20 @@ def represent_num_as_base(num, base):
 def hexdigest_as_JACK(string):
     if not string:
         return
-    return "".join(alphabet[c] for c in represent_num_as_base(int(string, 16), len(alphabet)))
+    return "".join(
+        AlphabetAccess.alphabet[c]
+        for c in represent_num_as_base(int(string, 16), len(AlphabetAccess.alphabet))
+    )
 
 
 def JACK_as_num(string: str):
     if isinstance(string, bytes):
         string = string.decode()
     string = "".join(string.split())
-    return sum(len(reverse_alphabet) ** i * reverse_alphabet[x] for i, x in enumerate(reversed(string)))
+    return sum(
+        len(AlphabetAccess.reverse_alphabet) ** i * AlphabetAccess.reverse_alphabet[x]
+        for i, x in enumerate(reversed(string))
+    )
 
 
 def num_as_hexdigest(num):
@@ -40659,4 +40672,4 @@ def num_as_hexdigest(num):
 
 
 def is_JACK(H):
-    return all(c in alphabet for c in H)
+    return all(c in AlphabetAccess.alphabet for c in H)
diff --git a/src/use/logutil.py b/src/use/logutil.py
new file mode 100755
index 00000000..55d70612
--- /dev/null
+++ b/src/use/logutil.py
@@ -0,0 +1,294 @@
+import io
+import logging
+import os
+import sys
+import time
+import traceback
+from logging import Formatter, PercentStyle, StreamHandler, StrFormatStyle, StringTemplateStyle
+from typing import NamedTuple, Deque
+from collections.abc import Callable
+
+import use
+
+BASIC_FORMAT: str = "%(levelname)s:%(name)s:%(message)s"
+
+_STYLES: dict[str, tuple[PercentStyle, str]] = {
+    "%": (PercentStyle, BASIC_FORMAT),
+    "{": (StrFormatStyle, "{levelname}:{name}:{message}"),
+    "$": (
+        StringTemplateStyle,
+        "\x1b[1;3${levelno}m${levelname}\x1b[1;30m: \x1b[0m\x1b[1;36m${name}\x1b[1;30m: \x1b[0m${message}",
+    ),
+}
+
+
+class TimeResult(
+    NamedTuple(
+        "TimeResult",
+        tm_year=int,
+        tm_mon=int,
+        tm_mday=int,
+        tm_hour=int,
+        tm_min=int,
+        tm_sec=int,
+        tm_wday=int,
+        tm_yday=int,
+        tm_isdst=bool,
+    )
+):
+    pass
+
+
+class ConsoleFormatter(Formatter):
+    """
+    Formatter instances are used to convert a LogRecord to text.
+
+    Formatters need to know how a LogRecord is constructed. They are
+    responsible for converting a LogRecord to (usually) a string which can
+    be interpreted by either a human or an external system. The base Formatter
+    allows a formatting string to be specified. If none is supplied, the
+    style-dependent default value, "%(message)s", "{message}", or
+    "${message}", is used.
+
+    The Formatter can be initialized with a format string which makes use of
+    knowledge of the LogRecord attributes - e.g. the default value mentioned
+    above makes use of the fact that the user's message and arguments are pre-
+    formatted into a LogRecord's message attribute. Currently, the useful
+    attributes in a LogRecord are described by:
+
+    %(name)s            Name of the logger (logging channel)
+    %(levelno)s         Numeric logging level for the message (DEBUG, INFO,
+                        WARNING, ERROR, CRITICAL)
+    %(levelname)s       Text logging level for the message ("DEBUG", "INFO",
+                        "WARNING", "ERROR", "CRITICAL")
+    %(pathname)s        Full pathname of the source file where the logging
+                        call was issued (if available)
+    %(filename)s        Filename portion of pathname
+    %(module)s          Module (name portion of filename)
+    %(lineno)d          Source line number where the logging call was issued
+                        (if available)
+    %(funcName)s        Function name
+    %(created)f         Time when the LogRecord was created (time.time()
+                        return value)
+    %(asctime)s         Textual time when the LogRecord was created
+    %(msecs)d           Millisecond portion of the creation time
+    %(relativeCreated)d Time in milliseconds when the LogRecord was created,
+                        relative to the time the logging module was loaded
+                        (typically at application startup time)
+    %(thread)d          Thread ID (if available)
+    %(threadName)s      Thread name (if available)
+    %(process)d         Process ID (if available)
+    %(message)s         The result of record.getMessage(), computed just as
+                        the record is emitted
+    """
+
+    converter: Callable[[int], TimeResult]
+    style: tuple[PercentStyle, str]
+    default_time_format: str = "%Y-%m-%d %H:%M:%S"
+    default_msec_format: str = "%s,%03d"
+    level = property(lambda self: logging.root.level)
+
+    def __init__(self):
+        """
+        Initialize the formatter
+        """
+        self.datefmt = "%Y-%m-%d"
+        style = "$"
+        fmt = _STYLES[style][1]
+        super(ConsoleFormatter, self).__init__(fmt=fmt, datefmt=self.datefmt, style=style, validate=True)
+
+    def formatTime(self, record, datefmt=None):
+        """
+        Return the creation time of the specified LogRecord as formatted text.
+
+        This method should be called from format() by a formatter which
+        wants to make use of a formatted time. This method can be overridden
+        in formatters to provide for any specific requirement, but the
+        basic behaviour is as follows: if datefmt (a string) is specified,
+        it is used with time.strftime() to format the creation time of the
+        record. Otherwise, an ISO8601-like (or RFC 3339-like) format is used.
+        The resulting string is returned. This function uses a user-configurable
+        function to convert the creation time to a tuple. By default,
+        time.localtime() is used; to change this for a particular formatter
+        instance, set the 'converter' attribute to a function with the same
+        signature as time.localtime() or time.gmtime(). To change it for all
+        formatters, for example if you want all logging times to be shown in GMT,
+        set the 'converter' attribute in the Formatter class.
+        """
+        if datefmt is None:
+            datefmt = self.datefmt
+        ct = self.converter(record.created)
+        if datefmt:
+            s = time.strftime(datefmt, ct)
+        else:
+            s = time.strftime(self.default_time_format, ct)
+            if self.default_msec_format:
+                s = self.default_msec_format % (s, record.msecs)
+        return s
+
+    def formatException(self, ei):
+        """
+        Format and return the specified exception information as a string.
+
+        This default implementation just uses
+        traceback.print_exception()
+        """
+        sio = io.StringIO()
+        tb = ei[2]
+        # See issues #9427, #1553375. Commented out for now.
+        # if getattr(self, 'fullstack', False):
+        #    traceback.print_stack(tb.tb_frame.f_back, file=sio)
+        traceback.print_exception(ei[0], ei[1], tb, None, sio)
+        s = sio.getvalue()
+        sio.close()
+        if s[-1:] == "\n":
+            s = s[:-1]
+        return s
+
+    def usesTime(self):
+        """
+        Check if the format uses the creation time of the record.
+        """
+        return True
+
+    def formatMessage(self, record):
+        return self._style.format(record)
+
+    def formatStack(self, stack_info):
+        """
+        This method is provided as an extension point for specialized
+        formatting of stack information.
+
+        The input data is a string as returned from a call to
+        :func:`traceback.print_stack`, but with the last trailing newline
+        removed.
+
+        The base implementation just returns the value passed in.
+        """
+        return stack_info
+
+    def format(self, record):
+        """
+        Format the specified record as text.
+
+        The record's attribute dictionary is used as the operand to a
+        string formatting operation which yields the returned string.
+        Before formatting the dictionary, a couple of preparatory steps
+        are carried out. The message attribute of the record is computed
+        using LogRecord.getMessage(). If the formatting string uses the
+        time (as determined by a call to usesTime(), formatTime() is
+        called to format the event time. If there is exception information,
+        it is formatted using formatException() and appended to the message.
+        """
+        record.message = record.getMessage()
+        if self.usesTime():
+            record.asctime = self.formatTime(record, self.datefmt)
+        s = self.formatMessage(record)
+        if record.exc_info and not record.exc_text:
+            record.exc_text = self.formatException(record.exc_info)
+        if record.exc_text:
+            if s[-1:] != "\n":
+                s = s + "\n"
+            s = s + record.exc_text
+        if record.stack_info:
+            if s[-1:] != "\n":
+                s = s + "\n"
+            s = s + self.formatStack(record.stack_info)
+        return s
+
+
+class ConsoleHandler(StreamHandler):
+    """
+    A handler class which writes logging records, appropriately formatted,
+    to a stream. Note that this class does not close the stream, as
+    sys.stdout or sys.stderr may be used.
+    """
+
+    terminator: str
+    stream: io.IOBase
+    formatter: ConsoleFormatter
+
+    def __init__(self):
+        """
+        Initialize the handler.
+        """
+        self.level = logging.root.level
+        formatter = ConsoleFormatter()
+        self.formatter = formatter
+        self.fmt = formatter
+        super(ConsoleHandler, self).__init__(stream=sys.stderr)
+        super(StreamHandler, self).__init__(level=logging.DEBUG)
+        self.formatter = formatter
+        self.fmt = formatter
+
+    def flush(self):
+        """
+        Flushes the stream.
+        """
+        self.acquire()
+        try:
+            if self.stream and hasattr(self.stream, "flush"):
+                self.stream.flush()
+        finally:
+            self.release()
+
+    def format(self, record):
+        """
+        Format the specified record.
+
+        If a formatter is set, use it. Otherwise, use the default formatter
+        for the module.
+        """
+        if self.formatter is None:
+            self.formatter = ConsoleFormatter()
+        fmt: ConsoleFormatter = self.formatter
+        return fmt.format(record)
+
+    def emit(self, record):
+        """
+        Emit a record.
+
+        If a formatter is specified, it is used to format the record.
+        The record is then written to the stream with a trailing newline.  If
+        exception information is present, it is formatted using
+        traceback.print_exception and appended to the stream.  If the stream
+        has an 'encoding' attribute, it is used to determine how to do the
+        output to the stream.
+        """
+        try:
+            msg = self.format(record)
+            stream = self.stream
+            # issue 35046: merged two stream.writes into one.
+            stream.write(msg + self.terminator)
+            self.flush()
+        except (RecursionError, Exception):  # See issue 36272
+            raise
+
+    def setStream(self, stream):
+        """
+        Sets the StreamHandler's stream to the specified value,
+        if it is different.
+
+        Returns the old stream, if the stream was changed, or None
+        if it wasn't.
+        """
+        if stream is self.stream:
+            result = None
+        else:
+            result = self.stream
+            self.acquire()
+            try:
+                self.flush()
+                self.stream = stream
+            finally:
+                self.release()
+        return result
+
+    def __repr__(self):
+        return f"<{self.__class__.__name__}>"
+
+
+if use.config.debugging:
+    logging.root.setLevel(logging.DEBUG)
+handler = ConsoleHandler()
+logging.root.handlers.append(handler)
diff --git a/src/use/main.py b/src/use/main.py
index 019914d4..adc48f01 100755
--- a/src/use/main.py
+++ b/src/use/main.py
@@ -9,23 +9,24 @@
 import importlib.util
 import inspect
 import os
+import shutil
 import sqlite3
 import sys
-import tempfile
 import threading
 import time
 import traceback
-from atexit import register
+from collections.abc import Callable
+from datetime import datetime
 from logging import DEBUG, INFO, NOTSET, getLogger, root
 from pathlib import Path
 from types import ModuleType
-from typing import Any, Callable, Optional, Type, Union
+from typing import Any, Optional, Union
 from warnings import warn
 
 import requests
 import toml
 from furl import furl as URL
-from icontract import ensure, invariant, require
+from icontract import require
 
 from use import (
     AmbiguityWarning,
@@ -40,13 +41,15 @@
     buffet_table,
     config,
     home,
-    isfunction,
+    sessionID,
 )
+from use.aspectizing import apply_aspect
 from use.hash_alphabet import JACK_as_num, is_JACK, num_as_hexdigest
-from use.messages import Message
-from use.pimp import _apply_aspect, _build_mod, _ensure_path, _fail_or_default, _parse_name
-from use.pypi_model import PyPI_Project, PyPI_Release, Version
+from use.messages import KwargMessage, StrMessage, TupleMessage, UserMessage
+from use.pimp import _build_mod, _ensure_path, _fail_or_default, _parse_name
+from use.pydantics import Version
 from use.tools import methdispatch
+from use.aspectizing import _applied_decorators
 
 log = getLogger(__name__)
 
@@ -58,11 +61,14 @@
 _using = {}
 
 # sometimes all you need is a sledge hammer..
-@register
 def _release_locks():
     for _ in range(2):
         [lock.unlock() for lock in threading._shutdown_locks]
         [reloader.stop() for reloader in _reloaders.values()]
+    log.info(f"### SESSION END {datetime.now().strftime('%Y/%m/%d %H:%M:%S')} {sessionID} ###")
+
+
+atexit.register(_release_locks)
 
 
 class ProxyModule(ModuleType):
@@ -93,22 +99,48 @@ def __setattr__(self, name, value):
         with self.__condition:
             setattr(self.__implementation, name, value)
 
-    def __matmul__(self, other: tuple):
+    def __matmul__(self, other: Callable):
         thing = self.__implementation
-        check, pattern, decorator = other
-        return _apply_aspect(thing, check, pattern, decorator, aspectize_dunders=False)
+        if not other:
+            raise NotImplementedError
+
+        # a little hack in order to be able to do `use @ numpy`...
+        if isinstance(self.__implementation, Use):
+            thing = other
+
+            def some_decorator(x):
+                return x
+
+            other = some_decorator
+
+        assert isinstance(other, Callable)
+
+        kwargs = {
+            "aspectize_dunders": True,
+            "excluded_types": {
+                ProxyModule,
+            },
+            "dry_run": True,
+        }
+
+        return apply_aspect(thing, other, **kwargs)
 
     def __call__(self, *args, **kwargs):
         with self.__condition:
             return self.__implementation(*args, **kwargs)
 
+    # to allow `numpy @ use` for a quick check
+    def __rmatmul__(self, *args, **kwargs):
+        return ProxyModule.__matmul__(self, *args, **kwargs)
+
 
 class ModuleReloader:
-    def __init__(self, *, proxy, name, path, initial_globals):
+    def __init__(self, *, proxy, name, path, package_name, initial_globals):
         self.proxy = proxy
         "ProxyModula that we refer to."
         self.name = name
         self.path = path
+        self.package_name = package_name
         self.initial_globals = initial_globals
         self._condition = threading.RLock()
         self._stopped = True
@@ -134,14 +166,14 @@ async def run_async(self):
             if current_filehash != last_filehash:
                 try:
                     mod = _build_mod(
-                        name=self.name,
+                        module_name=self.name,
                         code=code,
                         initial_globals=self.initial_globals,
                         module_path=self.path.resolve(),
                     )
                     self.proxy.__implementation = mod
                 except KeyError:
-                    print(traceback.format_exc())
+                    traceback.print_exc()
             last_filehash = current_filehash
             await asyncio.sleep(1)
 
@@ -155,14 +187,14 @@ def run_threaded(self):
                 if current_filehash != last_filehash:
                     try:
                         mod = _build_mod(
-                            name=self.name,
+                            module_name=self.name,
                             code=code,
                             initial_globals=self.initial_globals,
                             module_path=self.path,
                         )
                         self.proxy._ProxyModule__implementation = mod
                     except KeyError:
-                        print(traceback.format_exc())
+                        traceback.print_exc()
                 last_filehash = current_filehash
             time.sleep(1)
 
@@ -174,45 +206,24 @@ def __del__(self):
 
 
 class Use(ModuleType):
-    # MODES to reduce signature complexity
-    # enum.Flag wasn't viable, but this alternative is actually pretty cool
-    auto_install = Modes.auto_install
-    fatal_exceptions = Modes.fatal_exceptions
-    reloading = Modes.reloading
-    no_public_installation = Modes.no_public_installation
-
     def __init__(self):
-        # TODO for some reason removing self._using isn't as straight forward..
         self._using = _using
         # might run into issues during testing otherwise
         self.registry = self._set_up_registry()
         "Registry sqlite DB to store all relevant package metadata."
-        self._user_registry = toml.load(home / "user_registry.toml")
-        "User can override package directories via user_registry.toml"
-
-        # for the user to copy&paste
-        with open(home / "config_defaults.toml", "w") as rfile:
-            toml.dump(config, rfile)
-
-        with open(home / "config.toml") as rfile:
-            config.update(toml.load(rfile))
-
-        config.update(test_config)
 
-        if config["debugging"]:
+        if config.debugging:
             root.setLevel(DEBUG)
 
-        if config["version_warning"]:
+        if config.version_warning:
             try:
                 response = requests.get("https://pypi.org/pypi/justuse/json")
                 "Checking if there's a new version of justuse."
                 data = response.json()
                 max_version = max(Version(version) for version in data["releases"].keys())
-                target_version = max_version
-                this_version = __version__
-                if Version(this_version) < target_version:
+                if Version(__version__) < max_version:
                     warn(
-                        Message.use_version_warning(target_version),
+                        UserMessage.use_version_warning(max_version),
                         VersionWarning,
                     )
             except (KeyError, requests.exceptions.ConnectionError):
@@ -222,72 +233,78 @@ def __init__(self):
                     traceback.format_exc()
                 )  # we really don't need to bug the user about this (either pypi is down or internet is broken)
 
-    def _sqlite_row_factory(self, cursor, row):
-        return {col[0]: row[idx] for idx, col in enumerate(cursor.description)}
-
-    def _set_up_registry(self, path: Optional[Path] = None):
-        registry = None
-        if path or test_version and "DB_TEST" not in os.environ:
-            registry = sqlite3.connect(path or ":memory:").cursor()
-        else:
-            try:
-                registry = sqlite3.connect(home / "registry.db").cursor()
-            except Exception as e:
-                raise RuntimeError(Message.couldnt_connect_to_db(e))
-        registry.row_factory = self._sqlite_row_factory
+    def clean_slate(self):
+        shutil.rmtree(config.venv, ignore_errors=True)
+        shutil.rmtree(config.packages, ignore_errors=True)
+        config.venv.mkdir(mode=0o755, exist_ok=True)
+        config.packages.mkdir(mode=0o755, parents=True, exist_ok=True)
+        self.recreate_registry()
+
+    def _set_up_registry(self, *, registry=None, path: Optional[Path] = None):
+        # recreating reuses the registry connection and file
+        if registry is None:
+            if path or test_version and "DB_TEST" not in os.environ:
+                registry = sqlite3.connect(path or ":memory:").cursor()
+            else:
+                try:
+                    registry = sqlite3.connect(home / "registry.db").cursor()
+                except Exception as e:
+                    raise RuntimeError(UserMessage.couldnt_connect_to_db(e)) from e
+        registry.row_factory = lambda cursor, row: {
+            col[0]: row[idx] for idx, col in enumerate(cursor.description)
+        }
         registry.execute("PRAGMA foreign_keys=ON")
         registry.execute("PRAGMA auto_vacuum = FULL")
         registry.executescript(
             """
 CREATE TABLE IF NOT EXISTS "artifacts" (
-	"id"	INTEGER,
-	"distribution_id"	INTEGER,
-	"import_relpath" TEXT,
-	"artifact_path"	TEXT,
+    "id"    INTEGER,
+    "distribution_id"   INTEGER,
+    "import_relpath" TEXT,
+    "artifact_path" TEXT,
   "module_path" TEXT,
-	PRIMARY KEY("id" AUTOINCREMENT),
-	FOREIGN KEY("distribution_id") REFERENCES "distributions"("id") ON DELETE CASCADE
+    PRIMARY KEY("id" AUTOINCREMENT),
+    FOREIGN KEY("distribution_id") REFERENCES "distributions"("id") ON DELETE CASCADE
 );
 
 CREATE TABLE IF NOT EXISTS "distributions" (
-	"id"	INTEGER,
-	"name"	TEXT NOT NULL,
-	"version"	TEXT NOT NULL,
-	"installation_path"	TEXT,
-	"date_of_installation"	INTEGER,
-	"number_of_uses"	INTEGER,
-	"date_of_last_use"	INTEGER,
-	"pure_python_package"	INTEGER NOT NULL DEFAULT 1,
-	PRIMARY KEY("id" AUTOINCREMENT)
+    "id"    INTEGER,
+    "name"  TEXT NOT NULL,
+    "version"   TEXT NOT NULL,
+    "installation_path" TEXT,
+    "date_of_installation"  INTEGER,
+    "number_of_uses"    INTEGER,
+    "date_of_last_use"  INTEGER,
+    "pure_python_package"   INTEGER NOT NULL DEFAULT 1,
+    PRIMARY KEY("id" AUTOINCREMENT)
 );
 
 CREATE TABLE IF NOT EXISTS "hashes" (
-	"algo"	TEXT NOT NULL,
-	"value"	TEXT NOT NULL,
-	"artifact_id"	INTEGER NOT NULL,
-	PRIMARY KEY("algo","value"),
-	FOREIGN KEY("artifact_id") REFERENCES "artifacts"("id") ON DELETE CASCADE
+    "algo"  TEXT NOT NULL,
+    "value" INTEGER NOT NULL,
+    "artifact_id"   INTEGER NOT NULL,
+    PRIMARY KEY("algo","value"),
+    FOREIGN KEY("artifact_id") REFERENCES "artifacts"("id") ON DELETE CASCADE
 );
-
-CREATE TABLE IF NOT EXISTS "depends_on" (
-	"origin_path"	TEXT,
-	"target_path"	TEXT
-, "time_of_use"	INTEGER)
         """
         )
         registry.connection.commit()
         return registry
 
     def recreate_registry(self):
-        self.registry.close()
-        self.registry.connection.close()
-        self.registry = None
-        number_of_backups = len(list((home / "registry.db").glob("*.bak")))
-        (home / "registry.db").rename(home / f"registry.db.{number_of_backups + 1}.bak")
-        (home / "registry.db").touch(mode=0o644)
-        self.registry = self._set_up_registry()
+        number_of_backups = len(list(home.glob("registry.db*")))
+        shutil.copyfile(home / "registry.db", home / f"registry.db.{number_of_backups}.bak")
+        self._clear_registry()
+        self._set_up_registry(registry=self.registry)
         self.cleanup()
 
+    def _clear_registry(self):
+        for table in self.registry.execute("SELECT name FROM sqlite_schema WHERE type='table';").fetchall():
+            if table["name"] == "sqlite_sequence":
+                continue
+            self.registry.execute(f"DROP TABLE {table['name']};")
+            self.registry.connection.commit()
+
     def install(self):
         # yeah, really.. __builtins__ sometimes appears as a dict and other times as a module, don't ask me why
         if isinstance(__builtins__, dict):
@@ -315,9 +332,9 @@ def del_entry(self, name, version):
         )
         self.registry.execute(
             "DELETE FROM artifacts WHERE distribution_id IN (SELECT id FROM distributions WHERE name=? AND version=?)",
-            (name, version),
+            (name, str(version)),
         )
-        self.registry.execute("DELETE FROM distributions WHERE name=? AND version=?", (name, version))
+        self.registry.execute("DELETE FROM distributions WHERE name=? AND version=?", (name, str(version)))
         self.registry.connection.commit()
 
     def cleanup(self):
@@ -341,48 +358,13 @@ def delete_folder(path):
                 self.del_entry(name, version)
         self.registry.connection.commit()
 
-    def _save_module_info(
-        self,
-        *,
-        version: Version,
-        artifact_path: Optional[Path],
-        hash_value=Optional[str],
-        installation_path=Path,
-        module_path: Optional[Path],
-        name: str,
-        import_relpath: str,
-        hash_algo=Hash.sha256,
-    ):
-        """Update the registry to contain the pkg's metadata."""
-        if not self.registry.execute(
-            f"SELECT * FROM distributions WHERE name='{name}' AND version='{version}'"
-        ).fetchone():
-            self.registry.execute(
-                f"""
-INSERT INTO distributions (name, version, installation_path, date_of_installation, pure_python_package)
-VALUES ('{name}', '{version}', '{installation_path}', {time.time()}, {installation_path is None})
-"""
-            )
-            self.registry.execute(
-                f"""
-INSERT OR IGNORE INTO artifacts (distribution_id, import_relpath, artifact_path, module_path)
-VALUES ({self.registry.lastrowid}, '{import_relpath}', '{artifact_path}', '{module_path}')
-"""
-            )
-            self.registry.execute(
-                f"""
-INSERT OR IGNORE INTO hashes (artifact_id, algo, value)
-VALUES ({self.registry.lastrowid}, '{hash_algo.name}', '{hash_value}')"""
-            )
-        self.registry.connection.commit()
-
     def _set_mod(self, *, name, mod, frame, path=None, spec=None):
         """Helper to get the order right."""
         self._using[name] = ModInUse(name, mod, path, spec, frame)
 
     @methdispatch
     def __call__(self, thing, /, *args, **kwargs):
-        raise NotImplementedError(Message.cant_use(thing))
+        raise NotImplementedError(UserMessage.cant_use(thing))
 
     @require(lambda hash_algo: hash_algo in Hash)
     @require(lambda as_import: as_import.isidentifier())
@@ -400,32 +382,33 @@ def _use_url(
         modes=0,
     ) -> ProxyModule:
         log.debug(f"use-url: {url}")
-        exc = None
+        reckless = Modes.recklessness & modes
 
         response = requests.get(str(url))
         if response.status_code != 200:
-            raise ImportError(Message.web_error(url, response))
+            raise ImportError(UserMessage.web_error(url, response))
         this_hash = hash_algo.value(response.content).hexdigest()
-        if hash_value:
+
+        if hash_value and not reckless:
             if this_hash != hash_value:
                 return _fail_or_default(
                     UnexpectedHash(f"{this_hash} does not match the expected hash {hash_value} - aborting!"),
                     default,
                 )
         else:
-            warn(Message.no_validation(url, hash_algo, this_hash), NoValidationWarning)
+            warn(UserMessage.no_validation(url, hash_algo, this_hash), NoValidationWarning)
 
         name = url.path.segments[-1]
         try:
             mod = _build_mod(
-                name=name,
+                module_name=name,
                 code=response.content,
                 module_path=_ensure_path(url.path),
                 initial_globals=initial_globals,
             )
         except KeyError:
             raise
-        if exc:
+        if exc := None:
             return _fail_or_default(ImportError(exc), default)
 
         frame = inspect.getframeinfo(inspect.currentframe())
@@ -434,13 +417,13 @@ def _use_url(
             sys.modules[as_import] = mod
         return ProxyModule(mod)
 
-    @require(lambda as_import: as_import.isidentifier())
     @__call__.register(Path)
     def _use_path(
         self,
         path,
         /,
         *,
+        package_name=None,
         initial_globals=None,
         as_import: str = None,
         default=Modes.fastfail,
@@ -459,8 +442,9 @@ def _use_path(
         Returns:
             Optional[ModuleType]: The module if it was imported, otherwise whatever was specified as default.
         """
-        log.debug(f"use-path: {path}")
         initial_globals = initial_globals or {}
+        if as_import:
+            assert as_import.isidentifier()
 
         reloading = bool(Use.reloading & modes)
 
@@ -472,24 +456,38 @@ def _use_path(
 
         original_cwd = source_dir = Path.cwd()
         try:
-            if not path.is_absolute():
-                source_dir = Path.cwd()
-
             # calling from another use()d module
             # let's see where we started
             main_mod = __import__("__main__")
-            if source_dir and source_dir.exists():
-                os.chdir(source_dir.parent)
-                source_dir = source_dir.parent
-            else:
-                # there are a number of ways to call use() from a non-use() starting point
-                # let's first check if we are running in jupyter
-                jupyter = "ipykernel" in sys.modules
-                # we're in jupyter, we use the CWD as set in the notebook
-                if not jupyter and hasattr(main_mod, "__file__"):
-                    source_dir = (
-                        _ensure_path(inspect.currentframe().f_back.f_back.f_code.co_filename).resolve().parent
-                    )
+            # there are a number of ways to call use() from a non-use() starting point
+            # let's first check if we are running in jupyter
+            jupyter = "ipykernel" in sys.modules
+            # we're in jupyter, we use the CWD as set in the notebook
+            if not jupyter and hasattr(main_mod, "__file__"):
+                # problem: user wants to use.Path("some_file_in_the_same_dir")
+                # so we have to figure out where the file of the calling function is.
+                # but the *calling* function could also be a decorator, living completely elsewhere
+                # so we have to figure out whether we're being called by a decorator first.
+                # We use use.__call__ as landmark, because that's the official entry point
+                # and we can count on it being called. From there we need to check whether it was aspectized.
+                # If it was, we need to skip those decorators before finally get to the user code and
+                # we can actually see from where we've been called.
+                frame = inspect.currentframe()
+                while True:
+                    if frame.f_code == Use.__call__.__code__:
+                        break
+                    else:
+                        frame = frame.f_back
+                # a few more steps..
+                for x in _applied_decorators:
+                    frame = frame.f_back
+                try:
+                    # frame is in __call__ (or the last decorator we control), we need to step one more frame back
+                    source_dir = Path(frame.f_back.f_code.co_filename).resolve().parent
+                # we are being called from a shell like thonny, so we have to assume cwd
+                except OSError:
+                    source_dir = Path.cwd()
+
             if source_dir is None:
                 if main_mod.__loader__ and hasattr(main_mod.__loader__, "path"):
                     source_dir = _ensure_path(main_mod.__loader__.path).parent
@@ -505,21 +503,36 @@ def _use_path(
                     NotImplementedError("Can't determine a relative path from a virtual file."),
                     default,
                 )
-            path = source_dir.joinpath(path).resolve()
+            if not path.exists():
+                path = source_dir.joinpath(path).resolve()
             if not path.exists():
                 return _fail_or_default(ImportError(f"Sure '{path}' exists?"), default)
-            os.chdir(path.parent)
-            name = path.stem
+            if not path.is_absolute():
+                path = path.resolve()
+            try:
+                name = path.relative_to(source_dir)
+            except ValueError:
+                source_dir = path.parent
+                os.chdir(source_dir)
+                name = path.relative_to(source_dir)
+            ext = name.as_posix().rpartition(".")[-1]
+            name_as_path_with_ext = name.as_posix()
+            name_as_path = name_as_path_with_ext[: -len(ext) - (1 if ext else 0)]
+            name = name_as_path.replace("/", ".")
+            name_parts = name.split(".")
+            package_name = package_name or ".".join(name_parts[:-1])
+            module_name = path.stem  # sic!
             if reloading:
                 try:
                     with open(path, "rb") as rfile:
                         code = rfile.read()
                     # initial instance, if this doesn't work, just throw the towel
                     mod = _build_mod(
-                        name=name,
+                        module_name=module_name,
                         code=code,
                         initial_globals=initial_globals,
                         module_path=path.resolve(),
+                        package_name=package_name,
                     )
                 except KeyError:
                     exc = traceback.format_exc()
@@ -531,6 +544,7 @@ def _use_path(
                     name=name,
                     path=path,
                     initial_globals=initial_globals,
+                    package_name=package_name,
                 )
                 _reloaders[mod] = reloader
 
@@ -549,23 +563,24 @@ def _use_path(
                     reloader.start_threaded()
 
                 if not all(
-                    isfunction(value)
+                    inspect.isfunction(value)
                     for key, value in mod.__dict__.items()
                     if key not in initial_globals.keys() and not key.startswith("__")
                 ):
-                    warn(Message.not_reloadable(name), NotReloadableWarning)
+                    warn(UserMessage.not_reloadable(name), NotReloadableWarning)
             else:  # NOT reloading
                 with open(path, "rb") as rfile:
                     code = rfile.read()
                 # the path needs to be set before attempting to load the new module - recursion confusing ftw!
                 frame = inspect.getframeinfo(inspect.currentframe())
-                self._set_mod(name=name, mod=mod, frame=frame)
+                self._set_mod(name=module_name, mod=mod, frame=frame)
                 try:
                     mod = _build_mod(
-                        name=name,
+                        module_name=module_name,
                         code=code,
                         initial_globals=initial_globals,
                         module_path=path,
+                        package_name=package_name,
                     )
                 except KeyError:
                     del self._using[name]
@@ -591,7 +606,7 @@ def _use_kwargs(
         *,
         package_name: str = None,
         module_name: str = None,
-        version: str = None,
+        version: Optional[Union[Version, str]] = None,
         hash_algo=Hash.sha256,
         hashes: Optional[Union[str, list[str]]] = None,
         default=Modes.fastfail,
@@ -614,18 +629,19 @@ def _use_kwargs(
             RuntimeWarning: May be raised if the auto-installation of the pkg fails for some reason.
 
         Returns:
-            Optional[ModuleType]: Module if successful, default as specified otherwise.
+            ProxyModule|Any: Module if successful, default as specified otherwise.
         """
         log.debug(f"use-kwargs: {package_name} {module_name} {version} {hashes}")
         return self._use_package(
             name=f"{package_name}/{module_name}",
             package_name=package_name,
             module_name=module_name,
-            version=version,
+            version=Version(version) if version else None,
             hash_algo=hash_algo,
             hashes=hashes,
             default=default,
             modes=modes,
+            Message=KwargMessage,
         )
 
     @__call__.register(tuple)
@@ -634,7 +650,7 @@ def _use_tuple(
         pkg_tuple,
         /,
         *,
-        version: str = None,
+        version: Optional[Union[Version, str]] = None,
         hash_algo=Hash.sha256,
         hashes: Optional[Union[str, list[str]]] = None,
         default=Modes.fastfail,
@@ -657,7 +673,7 @@ def _use_tuple(
             RuntimeWarning: May be raised if the auto-installation of the pkg fails for some reason.
 
         Returns:
-            Optional[ModuleType]: Module if successful, default as specified otherwise.
+            ProxyModule|Any: Module if successful, default as specified otherwise.
         """
         log.debug(f"use-tuple: {pkg_tuple} {version} {hashes}")
         package_name, module_name = pkg_tuple
@@ -665,11 +681,12 @@ def _use_tuple(
             name=f"{package_name}/{module_name}",
             package_name=package_name,
             module_name=module_name,
-            version=version,
+            version=Version(version) if version else None,
             hash_algo=hash_algo,
             hashes=hashes,
             default=default,
             modes=modes,
+            Message=TupleMessage,
         )
 
     @__call__.register(str)
@@ -678,7 +695,7 @@ def _use_str(
         name: str,
         /,
         *,
-        version: str = None,
+        version: Optional[Union[Version, str]] = None,
         hash_algo=Hash.sha256,
         hashes: Optional[Union[str, list[str]]] = None,
         default=Modes.fastfail,
@@ -709,39 +726,47 @@ def _use_str(
             name=name,
             package_name=package_name,
             module_name=module_name,
-            version=version,
+            version=Version(version) if version else None,
             hash_algo=hash_algo,
             hashes=hashes,
             default=default,
             modes=modes,
+            Message=StrMessage,
         )
 
+    @require(lambda hash_algo: hash_algo != None)
     def _use_package(
         self,
         *,
         name,
-        package_name,
-        module_name,
-        version,
-        hashes,
-        modes,
-        default,
-        hash_algo,
-        user_msg=Message,
+        package_name: str,
+        module_name: str,
+        version: Optional[Version],
+        hashes: Optional[Union[str, set]],
+        default: Any,
+        hash_algo: Hash,
+        modes: int = 0,
+        Message: type = UserMessage,
     ):
+        auto_install = bool(Modes.auto_install & modes)
+        no_public_installation = bool(Modes.no_public_installation & modes)
+        fastfail = bool(Modes.fastfail & modes)
+        fatal_exceptions = bool(Modes.fatal_exceptions & modes)
+        no_browser = bool(Modes.no_browser & modes)
+        cleanup = not bool(Modes.no_cleanup & modes)
+
         if module_name:
             module_name = module_name.replace("/", ".").replace("-", "_")
-        assert hash_algo != None, "Hash algorithm must be specified"
 
+        # a single hash is a string
         if isinstance(hashes, str):
-            hashes = set(hashes.split())
+            # spaces are hard to see in JACK, so we ignore them
+            hashes = ["".join(hashes.split())]
         if not hashes:
             hashes = set()
-        hashes: set = {num_as_hexdigest(JACK_as_num(H)) if is_JACK(H) else H for H in hashes}
-        # we use boolean flags to reduce the complexity of the call signature
-        auto_install: bool = Modes.auto_install & modes
-
-        version: Version = Version(version) if version else None
+        hashes: set[int] = {
+            JACK_as_num(H) if is_JACK(H) else int(H, 16) for H in ("".join(H.split()) for H in hashes)
+        }
 
         # The "try and guess" behaviour is due to how classical imports work,
         # which is inherently ambiguous, but can't really be avoided for packages.
@@ -753,9 +778,9 @@ def _use_package(
         if name in self._using:
             spec = self._using[name].spec
         elif not auto_install:
-            spec = importlib.util.find_spec(package_name)
+            spec = importlib.util.find_spec(module_name.replace("-", "_"))
 
-        case = (bool(version), bool(hashes), bool(spec), bool(auto_install))
+        case = bool(version), bool(hashes), bool(spec), auto_install
         log.info("case = %s", case)
         # welcome to the buffet table, where everything is a lie
         kwargs = {
@@ -763,17 +788,22 @@ def _use_package(
             "package_name": package_name,
             "module_name": module_name,
             "version": version,
-            "hashes": hashes,
+            "user_provided_hashes": hashes,
             "hash_algo": hash_algo,
-            "user_msg": user_msg,
             "spec": spec,
-            "fastfail": Modes.fastfail & modes,
-            "no_public_installation": Modes.no_public_installation & modes,
-            "fatal_exceptions": Modes.fatal_exceptions & modes,
-            "sys_version": Version(".".join(map(str, sys.version_info[0:3]))),
+            "fastfail": fastfail,
+            "no_public_installation": no_public_installation,
+            "fatal_exceptions": fatal_exceptions,
+            "sys_version": Version(".".join(map(str, sys.version_info[:3]))),
+            "no_browser": no_browser,
+            "Message": Message,
+            "registry": self.registry,
+            "cleanup": cleanup,
         }
+
         result = buffet_table(case, kwargs)
         assert result
+        assert isinstance(result, (Exception, ModuleType))
 
         if isinstance(result, Exception):
             return _fail_or_default(result, default)
@@ -782,4 +812,3 @@ def _use_package(
             frame = inspect.getframeinfo(inspect.currentframe())
             self._set_mod(name=name, mod=result, spec=spec, frame=frame)
             return ProxyModule(result)
-        raise RuntimeError(f"{result=!r} is neither a module nor an Exception")
\ No newline at end of file
diff --git a/src/use/messages.py b/src/use/messages.py
index 7fe4dbeb..8ce02ce7 100755
--- a/src/use/messages.py
+++ b/src/use/messages.py
@@ -5,44 +5,139 @@
 """
 
 import webbrowser
+from collections import defaultdict, namedtuple
+from collections.abc import Callable
 from enum import Enum
+from pathlib import Path
+from shutil import copy
+from statistics import geometric_mean, median, stdev
 
-import use
-from use import __version__, config
-from use.pypi_model import Version
-
+from beartype import beartype
+from jinja2 import Environment, FileSystemLoader, select_autoescape
 
-def _web_no_version_or_hash_provided(*, name, package_name, version, hashes):
-    if not config["testing"]:
+import use
+from use import __version__, config, home
+from use.hash_alphabet import hexdigest_as_JACK
+from use.pydantics import PyPI_Release, Version
+
+env = Environment(
+    loader=FileSystemLoader(Path(__file__).parent / "templates"), autoescape=select_autoescape()
+)
+
+
+def std(times):
+    return stdev(times) if len(times) > 1 else 0
+
+
+def _web_tinny_profiler(timings):
+    copy(Path(__file__).absolute().parent / r"templates/profiling.css", home / "profiling.css")
+    DAT = namedtuple("DECORATOR", "qualname name min geom_mean median stdev len total")
+    timings_ = [
+        DAT(
+            getattr(
+                func, "__qualname__", getattr(func, "__module__", "") + getattr(func, "__name__", str(func))
+            ),
+            func.__name__,
+            min(times),
+            geometric_mean(times),
+            median(times),
+            std(times),
+            len(times),
+            sum(times),
+        )
+        for func, times in timings.items()
+    ]
+    with open(home / "profiling.html", "w", encoding="utf-8") as file:
+        args = {
+            "timings": timings_,
+        }
+        file.write(env.get_template("profiling.html").render(**args))
+    webbrowser.open(f"file://{home}/profiling.html")
+
+
+def _web_aspectized(decorators, functions):
+    copy(Path(__file__).absolute().parent / r"templates/aspects.css", home / "aspects.css")
+    DAT = namedtuple("DECORATOR", "name func")
+    redecorated = []
+    for ID, funcs in decorators.items():
+        name = functions[ID][-1].__name__
+        redecorated.append(DAT(name, funcs))
+
+    with open(home / "aspects.html", "w", encoding="utf-8") as file:
+        args = {
+            "decorators": redecorated,
+            "functions": functions,
+        }
+        file.write(env.get_template("aspects.html").render(**args))
+    webbrowser.open(f"file://{home}/aspects.html")
+
+
+@beartype
+def _web_aspectized_dry_run(
+    *, decorator: Callable, hits: list, check: Callable, pattern: str, module_name: str
+):
+    copy(Path(__file__).absolute().parent / r"templates/aspects.css", home / "aspects.css")
+    with open(home / "aspects_dry_run.html", "w", encoding="utf-8") as file:
+        args = {
+            "decorator": decorator,
+            "hits": hits,
+            "check": check,
+            "module_name": module_name,
+        }
+        file.write(env.get_template("aspects_dry_run.html").render(**args))
+    webbrowser.open(f"file://{home}/aspects_dry_run.html")
+
+
+def _web_pebkac_no_version_no_hash(*, name, package_name, version, no_browser: bool):
+    if not no_browser:
         webbrowser.open(f"https://snyk.io/advisor/python/{package_name}")
     return f"""Please specify version and hash for auto-installation of {package_name!r}.
-A webbrowser will open to the Snyk Advisor to check whether the package is vulnerable.
-If you want to auto-install the latest version:
-use("{name}", version="{version!s}", hashes={hashes!r}, modes=use.auto_install)"""
-
-
-def _web_pebkac_missing_hash(name, package_name, version, hashes):
-    if not config["testing"]:
-        with open(use.home / "web_exception.html", "w") as f:
-            f.write(
-                f"""<html><body>
-    <h1>{package_name!r}</h1>
-    {config}
-    <p>
-    Please specify the hash for auto-installation.
-    </p>
-    <p>
-    </p>
-    </body></html>"""
+{"" if no_browser else "A webbrowser should open to the Snyk Advisor to check whether the package is vulnerable or malicious."}
+If you want to auto-install the latest version, try the following line to select all viable hashes:
+use("{name}", version="{version!s}", modes=use.auto_install)"""
+
+
+@beartype
+def _web_pebkac_no_hash(
+    *,
+    name: str,
+    package_name: str,
+    version: Version,
+    releases: list[PyPI_Release],
+):
+    copy(Path(__file__).absolute().parent / r"templates/stylesheet.css", home / "stylesheet.css")
+    entry = namedtuple("Entry", "python platform hash_name hash_value jack_value")
+    table = defaultdict(lambda: [])
+    for rel in (rel for rel in releases if rel.version == version):
+        for hash_name, hash_value in rel.digests.items():
+            if hash_name not in (x.name for x in use.Hash):
+                continue
+            table[hash_name].append(
+                entry(
+                    rel.python_tag,
+                    rel.platform_tag,
+                    hash_name,
+                    hash_value,
+                    hexdigest_as_JACK(hash_value),
+                )
             )
-        webbrowser.open(f"file://{use.home / 'web_exception.html'}")
-    return f"""Failed to auto-install {package_name!r} because hashes aren't specified.
-        A webbrowser will open with a list of available hashes for different platforms.
-        If you only want to use the package on this platform, this may work:
-    use("{name}", version="{version!s}", hashes={hashes!r}, modes=use.auto_install)"""
 
+    with open(home / "web_exception.html", "w", encoding="utf-8") as file:
+        args = {
+            "name": name,
+            "package_name": package_name,
+            "version": version,
+            "table": table,
+        }
+        file.write(env.get_template("hash-presentation.html").render(**args))
+
+    # from base64 import b64encode
+    # def data_uri_from_html(html_string):
+    #    return f'data:text/html;base64,{b64encode(html_string.encode()).decode()}'
+    webbrowser.open(f"file://{home}/web_exception.html")
 
-class Message(Enum):
+
+class UserMessage(Enum):
     not_reloadable = (
         lambda name: f"Beware {name} also contains non-function objects, it may not be safe to reload!"
     )
@@ -73,54 +168,63 @@ class Message(Enum):
     ambiguous_name_warning = (
         lambda package_name: f"Attempting to load the pkg '{package_name}', if you rather want to use the local module: use(use._ensure_path('{package_name}.py'))"
     )
-    no_version_provided = (
-        lambda: "No version was provided, even though auto_install was specified! Trying to load classically installed pkg instead."
-    )
-    classically_imported = (
-        lambda name, this_version: f'Classically imported \'{name}\'. To pin this version: use("{name}", version="{this_version}")'
+    pebkac_missing_hash = (
+        lambda *, name, package_name, version, recommended_hash, no_browser: f"""Failed to auto-install {package_name!r} because hashes aren't specified.
+        {"" if no_browser else "A webbrowser should open with a list of available hashes for different platforms for you to pick."}"
+        If you want to use the package only on this platform, this should work:
+    use("{name}", version="{version!s}", hashes={recommended_hash!r}, modes=use.auto_install)"""
     )
-    pebkac_missing_hash = _web_pebkac_missing_hash
     pebkac_unsupported = (
         lambda package_name: f"We could not find any version or release for {package_name} that could satisfy our requirements!"
     )
     pip_json_mess = (
         lambda package_name, target_version: f"Tried to auto-install {package_name} {target_version} but failed because there was a problem with the JSON from PyPI."
     )
-    no_version_or_hash_provided = _web_no_version_or_hash_provided
+    pebkac_no_version_no_hash = _web_pebkac_no_version_no_hash
     cant_import = lambda name: f"No pkg installed named {name} and auto-installation not requested. Aborting."
     cant_import_no_version = (
         lambda package_name: f"Failed to auto-install '{package_name}' because no version was specified."
     )
-    venv_unavailable = (
-        lambda python_exe, python_version, python_platform: f"""
-Your system does not have a usable 'venv' pkg for this version of Python:
-   Path =     {python_exe}
-   Version =  {python_version}
-   Platform = {python_platform}
-
-Please run:
-   sudo apt update
-   sudo apt install python3-venv
-to install the necessary packages.
-
-You can test if your version of venv is working by running:
-  {python_exe} -m venv testenv && ls -lA testenv/bin/python
-"""
-    )
+
     no_distribution_found = (
-        lambda package_name, version: f"Failed to find any distribution for {package_name} with version {version} that can be run this platform!"
+        lambda package_name, version, last_version: f"Failed to find any distribution for {package_name} version {version} that can be run on this platform. (For your information, the most recent version of {package_name} is {last_version})"
+    )
+
+    no_recommendation = (
+        lambda package_name, version: f"We could not find any release for {package_name} {version} that appears to be compatible with this platform. Check your browser for a list of hashes and select manually."
     )
 
 
-class StrMessage(Message):
+class StrMessage(UserMessage):
     cant_import = (
         lambda package_name: f"No pkg installed named {package_name} and auto-installation not requested. Aborting."
     )
 
 
-class TupleMessage(Message):
+class TupleMessage(UserMessage):
     pass
 
 
-class KwargsMessage(Message):
+class KwargMessage(UserMessage):
     pass
+
+
+def _web_aspectizing_overview(*, decorator, check, pattern, visited, hits):
+    msg = f"""
+<html>
+<head>
+<script src="/brython.js"></script>
+</head>
+<body>
+<ul>
+    {''.join(f"<li>{h}</li>" for h in hits)}
+</ul>
+</body>
+</html>
+"""
+
+    with open(use.home / "aspectizing_overview.html", "w") as f:
+        f.write(msg)
+    if not config.testing:
+        webbrowser.open(use.home / "aspectizing_overview.html")
+    return msg
diff --git a/src/use/pimp.py b/src/use/pimp.py
index 951ee1b4..d689e8ea 100755
--- a/src/use/pimp.py
+++ b/src/use/pimp.py
@@ -2,55 +2,60 @@
 Delegating package installation to pip, packaging and friends.
 """
 
+
+
 import codecs
+import contextlib
 import importlib.util
 import linecache
 import os
 import platform
 import re
-import shlex
-import sqlite3
 import sys
 import tarfile
+import time
+import traceback
 import zipfile
 import zipimport
+from collections.abc import Callable
 from functools import lru_cache as cache
 from functools import reduce
 from importlib import metadata
 from importlib.machinery import ModuleSpec, SourceFileLoader
-from importlib.metadata import PackageNotFoundError
-from itertools import chain
+from importlib.metadata import Distribution, PackageNotFoundError, PackagePath
+from itertools import chain, product
 from logging import getLogger
+from os.path import abspath, split
 from pathlib import Path, PureWindowsPath, WindowsPath
-from pprint import pformat
-from subprocess import run
+from shutil import rmtree
+from sqlite3 import Cursor
+from subprocess import CalledProcessError, run
 from types import ModuleType
-from typing import Any, Callable, Optional, Protocol, TypeVar, Union, runtime_checkable
+from typing import (Any, Iterable, Optional, Protocol, TypeVar, Union,
+                    runtime_checkable)
 from warnings import catch_warnings, filterwarnings, warn
 
 import furl
 import packaging
 import requests
+from beartype import beartype
 from furl import furl as URL
 from icontract import ensure, require
 from packaging import tags
 from packaging.specifiers import SpecifierSet
 
-
-import use
-from use import Hash, Modes, VersionWarning, config
+from use import (Hash, InstallationError, Modes, UnexpectedHash,
+                 VersionWarning, config, sessionID)
 from use.hash_alphabet import JACK_as_num, hexdigest_as_JACK, num_as_hexdigest
-from use.messages import Message, _web_no_version_or_hash_provided
-from use.pypi_model import PyPI_Project, PyPI_Release, Version, _delete_none
+from use.messages import (UserMessage, _web_pebkac_no_hash,
+                          _web_pebkac_no_version_no_hash)
+from use.pydantics import PyPI_Project, PyPI_Release, RegistryEntry, Version
 from use.tools import pipes
 
 log = getLogger(__name__)
 
 
-T = TypeVar("T", bound=Callable[[str, str, Version, set[str]], str])
-@runtime_checkable
-class MissingHashFotmatter(Protocol[T]):
-    pass
+T = TypeVar("T")
 
 
 class PlatformTag:
@@ -71,6 +76,7 @@ def __eq__(self, other):
         return self.platform == other.platform
 
 
+@beartype
 def _ensure_version(
     result: Union[ModuleType, Exception], *, name, version, **kwargs
 ) -> Union[ModuleType, Exception]:
@@ -78,7 +84,7 @@ def _ensure_version(
         return result
     result_version = _get_version(mod=result)
     if result_version != version:
-        warn(Message.version_warning(name, version, result_version), category=VersionWarning)
+        warn(UserMessage.version_warning(name, version, result_version), category=VersionWarning)
     return result
 
 
@@ -99,18 +105,9 @@ def _ensure_path(value: Union[bytes, str, furl.Path, Path]) -> Path:
 # fmt: on
 
 
-def execute_wrapped(sql: str, params: tuple):
-    ___use = getattr(sys, "modules").get("use")
-    try:
-        return getattr(___use, "registry").execute(sql, params)
-    except sqlite3.OperationalError as _oe:
-        pass
-    getattr(___use, "recreate_registry")()
-    return getattr(___use, "registry").execute(sql, params)
-
-
 @cache
-def get_supported() -> frozenset[PlatformTag]: # cov: exclude
+@beartype
+def get_supported() -> frozenset[PlatformTag]:  # cov: exclude
     """
     Results of this function are cached. They are expensive to
     compute, thanks to some heavyweight usual players
@@ -123,50 +120,31 @@ def get_supported() -> frozenset[PlatformTag]: # cov: exclude
     get_supported = None
     with catch_warnings():
         filterwarnings(action="ignore", category=DeprecationWarning)
-        try:
-          from pip._internal.resolution.legacy.resolver import get_supported
-        except ImportError:
-          pass
+        with contextlib.suppress(ImportError):
+            from pip._internal.resolution.legacy.resolver import get_supported
         if not get_supported:
-          try:
-            from pip._internal.models.target_python import get_supported
-          except ImportError:
-            pass
+            with contextlib.suppress(ImportError):
+                from pip._internal.models.target_python import get_supported
         if not get_supported:
-          try:
-            from pip._internal.utils.compatibility_tags import get_supported
-          except ImportError:
-            pass
+            with contextlib.suppress(ImportError):
+                from pip._internal.utils.compatibility_tags import \
+                    get_supported
         if not get_supported:
-          try:
-            from pip._internal.resolution.resolvelib.factory import get_supported
-          except ImportError:
-            pass
-    
+            with contextlib.suppress(ImportError):
+                from pip._internal.resolution.resolvelib.factory import \
+                    get_supported
     get_supported = get_supported or (lambda: [])
 
-    items: list[PlatformTag] = [
-        PlatformTag(platform=tag.platform) for tag in get_supported()
-    ]
+    items: list[PlatformTag] = [PlatformTag(platform=tag.platform) for tag in get_supported()]
 
-    for tag in packaging.tags._platform_tags():
-        items.append(PlatformTag(platform=str(tag)))
+    items.extend(PlatformTag(platform=str(tag)) for tag in packaging.tags._platform_tags())
 
     return frozenset(items)
 
 
-def _filter_by_version(project_: "PyPI_Project", version: str) -> "PyPI_Project":
-    v = Version(version)
-    rels = project_.releases.get(v, project_.releases.get(version, []))
-
-    if not rels:
-      return project_
-
-    project_.releases = { v: rels }
-    project_.urls = rels
-    print(repr(project_))
-
-    return project_
+@beartype
+def _filter_by_version(releases: list[PyPI_Release], *, version: Version) -> list[PyPI_Release]:
+    return list(filter(lambda r: r.version == version, releases))
 
 
 class ZipFunctions:
@@ -198,10 +176,10 @@ def read_entry(self, entry_name):
             return (Path(entry_name).stem, text)
 
 
+@beartype
 @pipes
 def archive_meta(artifact_path):
     DIST_PKG_INFO_REGEX = re.compile("(dist-info|-INFO|\\.txt$|(^|/)[A-Z0-9_-]+)$")
-    meta = archive = names = functions = None
 
     if ".tar" in Path(str(artifact_path)).stem:
         functions = TarFunctions(artifact_path)
@@ -209,7 +187,7 @@ def archive_meta(artifact_path):
         functions = ZipFunctions(artifact_path)
 
     archive, names = functions.get()
-    meta = dict(names << filter(DIST_PKG_INFO_REGEX.search) << map(functions.read_entry))
+    meta = names << filter(DIST_PKG_INFO_REGEX.search) << map(functions.read_entry) >> dict
     meta.update(
         dict(
             (lp := l.partition(": "), (lp[0].lower().replace("-", "_"), lp[2]))[-1]
@@ -238,13 +216,14 @@ def archive_meta(artifact_path):
     ):
         meta["import_relpath"] = relpath
         break
+    else:
+        meta["import_relpath"] = f"{import_name}.py"
     archive.close()
     return meta
 
 
+@beartype
 def _clean_sys_modules(package_name: str) -> None:
-    if not package_name:
-        return
     for k in dict(
         [
             (k, v)
@@ -260,20 +239,12 @@ def _clean_sys_modules(package_name: str) -> None:
             del sys.modules[k]
 
 
-def _venv_root(package_name, version, home) -> Path:
-    assert version
-    return home / "venv" / package_name / str(version)
-
-
+@beartype
 def _pebkac_no_version(
     *,
     name: str,
     func: Callable[..., Union[Exception, ModuleType]] = None,
-    version: Version = None,
-    hash_algo=None,
-    package_name: str = None,
-    module_name: str = None,
-    message_formatter: MissingHashFotmatter = Message.pebkac_missing_hash,
+    Message: type,
     **kwargs,
 ) -> Union[ModuleType, Exception]:
 
@@ -281,120 +252,177 @@ def _pebkac_no_version(
         result = func()
         if isinstance(result, (Exception, ModuleType)):
             return result
+        assert False, f"{func}() returned {result!r}"
 
     return RuntimeWarning(Message.cant_import_no_version(name))
 
 
+@beartype
+@pipes
 def _pebkac_no_hash(
     *,
-    version: Version = None,
+    name: str,
+    version: Version,
+    package_name: str,
+    no_browser: bool,
+    Message: type,
     hash_algo: Hash,
-    package_name: str = None,
     **kwargs,
-) -> Union[Exception, ModuleType]:
-    hashes = {
-        hexdigest_as_JACK(entry.digests.get(hash_algo.name))
-        for entry in _get_package_data(package_name).releases[version]
-    }
-    if hashes:
-        return RuntimeWarning(Message.pebkac_missing_hash(name=package_name, package_name=package_name, version=version, hashes=hashes))
-    else:
-        return RuntimeWarning(Message.no_distribution_found(package_name, version))
+) -> RuntimeWarning:
+    project = _get_data_from_pypi(package_name=package_name, version=version)
+    releases = _get_releases(project)
+
+    if version not in (r.version for r in releases):
+        last_version = next(iter(reversed(project.releases)))
+        return RuntimeWarning(Message.no_distribution_found(package_name, version, last_version))
+
+    project = _filter_by_version(releases, version=version)
+    filtered = _filter_by_platform(
+        releases,
+        tags=get_supported(),
+    )
+    ordered = _sort_releases(filtered)
+
+    if not no_browser:
+        _web_pebkac_no_hash(name=name, package_name=package_name, version=version, releases=ordered)
+
+    if not ordered:
+        return RuntimeWarning(Message.no_recommendation(package_name, version))
 
+    recommended_hash = hexdigest_as_JACK(ordered[0].digests.get(hash_algo.name))
 
+    # for test_suggestion_works, don't remove
+    print(recommended_hash)
+
+    return RuntimeWarning(
+        Message.pebkac_missing_hash(
+            name=name,
+            package_name=package_name,
+            version=version,
+            recommended_hash=recommended_hash,
+            no_browser=no_browser,
+        )
+    )
+
+
+@beartype
 @pipes
 def _pebkac_no_version_no_hash(
     *,
     name: str,
-    hash_algo: Hash,
     package_name: str,
+    no_browser: bool,
+    Message: type,
     **kwargs,
 ) -> Exception:
     # let's try to make an educated guess and give a useful suggestion
-    proj = _get_package_data(package_name)
-    ordered = _filtered_and_ordered_data(proj, version=None)
-    # we tried our best, but we didn't find anything that could work'
+    proj = _get_data_from_pypi(package_name=package_name)
+    releases = _get_releases(proj)
+
+    ordered = releases >> _filter_by_platform(tags=get_supported()) >> _sort_releases
+    # we tried our best, but we didn't find anything that could work
+
+    # let's try to find *anything*
     if not ordered:
-        return RuntimeWarning(Message.pebkac_unsupported(package_name))
+        ordered = _sort_releases(releases)
+        if not ordered:
+            # we tried our best..
+            return RuntimeWarning(Message.pebkac_unsupported(package_name))
+
+        if not no_browser:
+            version = ordered[-1].version
+            _web_pebkac_no_hash(name=name, package_name=package_name, version=version, project=proj)
+            return RuntimeWarning(
+                Message.pebkac_no_version_no_hash(
+                    name=name, package_name=package_name, version=version, no_browser=no_browser
+                )
+            )
+
+    recommended_version = ordered[0].version
+    # for test_suggestion_works, don't remove (unless you want to work out the details)
+    print(recommended_version)
+
     # we found something that could work, but it may not fit to the user's requirements
-    version = ordered[0].version
-    hash = ordered[0].digests.get(hash_algo.name)
-    hashes = { hexdigest_as_JACK(hash) }
     return RuntimeWarning(
-        Message.no_version_or_hash_provided(
+        Message.pebkac_no_version_no_hash(
             name=name,
-            hashes=hashes,
             package_name=package_name,
-            version=version,
+            version=recommended_version,
+            no_browser=no_browser,
         )
     )
 
 
+@beartype
 def _import_public_no_install(
     *,
-    func: Callable[..., Union[Exception, ModuleType]] = None,
-    package_name: str = None,
-    module_name: str = None,
-    spec=None,
+    module_name: str,
     **kwargs,
 ) -> Union[Exception, ModuleType]:
-    if func:
-        result = func()
-        if isinstance(result, (Exception, ModuleType)):
-            return result
     # builtin?
     builtin = False
     try:
-        metadata.PathDistribution.from_name(package_name)
+        metadata.PathDistribution.from_name(module_name)
     except metadata.PackageNotFoundError:  # indeed builtin!
         builtin = True
+    # TODO ehhh.. builtin needs to be handled differently?
 
-    if builtin:
-        if spec.name in sys.modules:
-            mod = sys.modules[spec.name]
-        if mod is None:
-            mod = importlib.import_module(module_name)
-        assert mod
-        sys.modules[spec.name] = mod
-        spec.loader.exec_module(mod)  # ! => cache
-        return mod
-    # it seems to be installed in some way, for instance via pip
-    return importlib.import_module(module_name)  # ! => cache
+    mod = sys.modules.get(module_name)
+    imported = bool(mod)
 
+    if not mod:
+        mod = importlib.import_module(module_name)
 
-def _parse_name(name) -> tuple[str, str]:
-    match = re.match(r"(?P<package_name>[^/.]+)/?(?P<rest>[a-zA-Z0-9._]+)?$", name)
-    assert match, f"Invalid name spec: {name!r}"
-    names = match.groupdict()
-    package_name = names["package_name"]
-    rest = names["rest"]
-    if not package_name:
-        package_name = rest
-    if not rest:
-        rest = package_name
-    return (package_name, rest)
+    # # ? does this solve the caching issue?
+    # if not imported:
+    #     del sys.modules[module_name]
+    return mod
 
 
-def _auto_install(
-    *,
-    name: str,
-    func: Callable[..., Union[Exception, ModuleType]] = None,
-    version: Version,
-    hash_algo,
-    package_name: str,
-    module_name: str,
-    **kwargs,
-) -> Union[ModuleType, BaseException]:
-    if func:
-        result = func()
-        if isinstance(result, (Exception, ModuleType)):
-            return result
+def _parse_name(name: str) -> tuple[str, str]:
+    """Parse the user-provided name into a package name for installation and a module name for import.
+
+    The package name is whatever pip would expect.
+    The module name is whatever import would expect.
+
+    Mini-DSL: / separates the package name from the module name.
+    """
+    if not name:
+        return None, None
+
+    def old():
+        # as fallback
+        match = re.match(r"(?P<package_name>[^/.]+)/?(?P<rest>[a-zA-Z0-9._]+)?$", name)
+        assert match, f"Invalid name spec: {name!r}"
+        names = match.groupdict()
+        package_name = names["package_name"]
+        rest = names["rest"]
+        if not package_name:
+            package_name = rest
+        if not rest:
+            rest = package_name
+        return package_name
+
+    package_name = None
+    module_name = ""
+    if "/" in name:
+        if name.count("/") > 1:
+            raise ImportError(
+                f"Invalid name spec: {name!r}, can't have multiple / characters as package/module separator."
+            )
+        package_name, _, module_name = name.partition("/")
+    else:
+        package_name = old()
+        module_name = name
+    return (package_name, module_name)
 
-    query = execute_wrapped(
+
+@beartype
+def _check_db_for_installation(*, registry=Cursor, package_name=str, version) -> Optional[RegistryEntry]:
+    query = registry.execute(
         """
         SELECT
-            artifacts.id, import_relpath,
-            artifact_path, installation_path, module_path
+            artifact_path, installation_path, pure_python_package
         FROM distributions
         JOIN artifacts ON artifacts.id = distributions.id
         WHERE name=? AND version=?
@@ -405,257 +433,263 @@ def _auto_install(
             str(version),
         ),
     ).fetchone()
+    return RegistryEntry(**query) if query else None
 
-    if not query or not _ensure_path(query["artifact_path"]).exists():
-        query = _find_or_install(package_name, version)
 
-    artifact_path = _ensure_path(query["artifact_path"])
-    module_path = _ensure_path(query["module_path"])
-    # trying to import directly from zip
-    _clean_sys_modules(module_name)
-    try:
-        importer = zipimport.zipimporter(artifact_path)
-        return importer.load_module(query["import_name"])
-    except BaseException as zerr:
-        pass
-    orig_cwd = Path.cwd()
-    mod = None
-    if "installation_path" not in query or not _ensure_path(query["installation_path"]).exists():
-        if query:
-            sys.modules["use"].del_entry(name, version)
-        query = _find_or_install(package_name, version, force_install=True)
-        artifact_path = _ensure_path(query["artifact_path"])
-        module_path = _ensure_path(query["module_path"])
-    assert "installation_path" in query  # why redundant assertions?
-    assert query["installation_path"]
-    installation_path = _ensure_path(query["installation_path"])
-    try:
-        module_path = _ensure_path(query["module_path"])
-        os.chdir(installation_path)
-        import_name = (
-            str(module_path.relative_to(installation_path))
-            .replace("\\", "/")
-            .replace("/__init__.py", "")
-            .replace("-", "_")
-        )
-        return (
-            mod := _load_venv_entry(
-                package_name=package_name,
+@beartype
+def _auto_install(
+    *,
+    package_name: str,
+    module_name: str,
+    func: Callable[..., Union[Exception, ModuleType]] = None,
+    version: Version,
+    hash_algo: Hash,
+    user_provided_hashes: set[int],
+    registry: Cursor,
+    cleanup: bool,
+    **kwargs,
+) -> Union[ModuleType, BaseException]:
+    """Install, if necessary, the package and import the module in any possible way."""
+    if func:
+        result = func()
+        if isinstance(result, (Exception, ModuleType)):
+            return result
+        else:
+            raise AssertionError(f"{func!r} returned {result!r}")
+
+    if entry := _check_db_for_installation(registry=registry, package_name=package_name, version=version):
+        # is there a point in checking the hashes at this point? probably not.
+        if entry.pure_python_package:
+            assert entry.artifact_path.exists()
+            # let's not try to catch this - since this apparently already worked *at least once* - no fallback
+            return zipimport.zipimporter(entry.artifact_path).load_module(module_name)
+        # else: we have an installed package, let's try to import it
+        original_cwd = Path.cwd()
+        assert entry.installation_path.exists()
+        os.chdir(entry.installation_path)
+        # with an installed package there may be weird issues we can't be sure about so let's be safe
+        try:
+            return _load_venv_entry(
                 module_name=module_name,
-                module_path=module_path,
-                installation_path=installation_path,
+                installation_path=entry.installation_path,
             )
+        except BaseException as err:
+            traceback.print_exc(file=sys.stderr)
+            msg = err
+        finally:
+            os.chdir(original_cwd)
+        return ImportError(msg)
+
+    # else: we have to download the package and install it
+    project = _get_data_from_pypi(package_name=package_name, version=version)
+    # we *did* ask the user to give us hashes of artifacts that *should* work, so let's check for those.
+    # We can't be sure which one of those hashes will work on this platform, so let's try all of them.
+
+    for H in user_provided_hashes:
+        log.info(f"Attempting auto-installation of <{num_as_hexdigest(H)}>...")
+        url = next(
+            (URL(purl.url) for purl in project.urls if H == int(purl.digests[hash_algo.name], 16)),
+            None,
         )
 
-    finally:
-        os.chdir(orig_cwd)
-        if "fault_inject" in config:
-            config["fault_inject"](**locals())
-        if mod:
-            sys.modules["use"]._save_module_info(
-                name=package_name,
-                import_relpath=str(_ensure_path(module_path).relative_to(installation_path)),
-                version=version,
-                artifact_path=artifact_path,
-                hash_value=hash_algo.value(artifact_path.read_bytes()).hexdigest(),
-                module_path=module_path,
-                installation_path=installation_path,
+        if not url:
+            return UnexpectedHash(
+                f"user provided {user_provided_hashes}, do not match any of the {len(project.urls)} possible artifacts"
             )
 
-
-def _process(*argv, env={}):
-    _realenv = {
-        k: v for k, v in chain(os.environ.items(), env.items()) if isinstance(k, str) and isinstance(v, str)
-    }
-    o = run(
-        **(
-            setup := dict(
-                executable=(exe := argv[0]),
-                args=[*map(str, argv)],
-                bufsize=1024,
-                input="",
-                capture_output=False,
-                timeout=45000,
-                check=True,
-                close_fds=True,
-                env=_realenv,
-                encoding="ISO-8859-1",
-                errors="ISO-8859-1",
-                text=True,
-                shell=False,
+        # got an url for an artifact with a hash given by the user, let's install it
+        filename = url.asdict()["path"]["segments"][-1]
+        artifact_path = config.packages / filename
+        _download_artifact(artifact_path=artifact_path, url=url, hash_value=H, hash_algo=hash_algo)
+        try:
+            log.info("Attempting to install..")
+            entry = _install(
+                package_name=package_name,
+                artifact_path=artifact_path,
+                version=version,
+                force_install=True,
             )
-        )
-    )
-    if o.returncode == 0:
-        return o
-    raise RuntimeError(  # cov: exclude
-        "\x0a".join(
-            (
-                "\x1b[1;41;37m",
-                f"Problem running--command exited with non-zero: {o.returncode}",
-                f"{shlex.join(map(str, setup['args']))}",
-                "---[  Errors  ]---",
-                f"{o.stderr or o.stdout}",
-                "\x1b[0;1;37m",
-                "Arguments to subprocess.run(**setup):",
-                f"{pformat(setup, indent=2, width=70, compact=False)}",
-                "---[  STDOUT  ]---",
-                f"{o.stdout}",
-                "---[  STDERR  ]---",
-                f"{o.stderr}\x1b[0m",
+            # packages like tensorflow-gpu only need to be installed but nothing imported, so module name is empty
+            log.info("Attempting to import...")
+            mod = _load_venv_entry(
+                module_name=module_name,
+                installation_path=entry.installation_path,
             )
+            log.info("Successfully imported.")
+        except BaseException as err:
+            msg = err  # sic
+            log.error(err)
+            traceback.print_exc(file=sys.stderr)
+            if entry and cleanup:
+                rmtree(entry.installation_path)
+                assert not entry.installation_path.exists()
+            continue
+
+        _save_package_info(
+            package_name=package_name,
+            version=version,
+            artifact_path=entry.artifact_path,
+            hash_value=int(hash_algo.value(entry.artifact_path.read_bytes()).hexdigest(), 16),
+            hash_algo=hash_algo,
+            installation_path=entry.installation_path,
+            registry=registry,
         )
-        if o.returncode != 0
-        else (f"{o.stdout}\n\n{o.stderr}")
+        return mod
+    log.critical(
+        f"Could not install {package_name!r} {version!r}. Hashes that were attempted: {[num_as_hexdigest(H) for H in user_provided_hashes]}"
     )
+    return ImportError(msg)
 
 
-def _get_venv_env(venv_root: Path) -> dict[str, str]:
-    pathvar = os.environ.get("PATH")
-    python_exe = Path(sys.executable)
-    if not venv_root.exists():
-        venv_root.mkdir(parents=True)
-    exe_dir = python_exe.parent.absolute()
-    return {}
+@beartype
+def _save_package_info(
+    *,
+    registry=Cursor,
+    version: Version,
+    artifact_path: Path,
+    installation_path: Path,
+    hash_value=int,
+    hash_algo: Hash,
+    package_name: str,
+):
+    """Update the registry to contain the pkg's metadata."""
+    if not registry.execute(
+        f"SELECT * FROM distributions WHERE name='{package_name}' AND version='{version}'"
+    ).fetchone():
+        registry.execute(
+            f"""
+INSERT INTO distributions (name, version, installation_path, date_of_installation, pure_python_package)
+VALUES ('{package_name}', '{version}', '{installation_path}', {time.time()}, {installation_path is None})
+"""
+        )
+        registry.execute(
+            f"""
+INSERT OR IGNORE INTO artifacts (distribution_id, artifact_path)
+VALUES ({registry.lastrowid}, '{artifact_path}')
+"""
+        )
+        registry.execute(
+            f"""
+INSERT OR IGNORE INTO hashes (artifact_id, algo, value)
+VALUES ({registry.lastrowid}, '{hash_algo.name}', '{hash_value}')"""
+        )
+    registry.connection.commit()
 
 
-@ensure(lambda url: str(url).startswith("http"))  # should we be enforcing 443 > 80?
-def _download_artifact(name, version, filename, url) -> Path:
-    artifact_path = (sys.modules["use"].home / "packages" / filename).absolute()
-    if not artifact_path.exists():
+@beartype
+@ensure(lambda url: str(url).startswith("http"))
+def _download_artifact(*, artifact_path: Path, url: URL, hash_algo: Hash, hash_value: int):
+    # let's check if we downloaded it already, just in case
+    if (
+        artifact_path.exists()
+        and int(hash_algo.value(artifact_path.read_bytes()).hexdigest(), 16) == hash_value
+    ):
+        log.info("Artifact already downloaded. Hashes matching.")
+        return
+    # this should work since we just got the url from pypi itself - if this fails, we have bigger problems
+    log.info("Downloading artifact from PyPI...")
+    data = requests.get(url).content
+    artifact_path.write_bytes(data)
+    if int(hash_algo.value(artifact_path.read_bytes()).hexdigest(), 16) != hash_value:
+        # let's try once again, cosmic rays and all, believing in pure dumb luck
+        log.info("Artifact downloaded but hashes don't match. Trying again...")
         data = requests.get(url).content
         artifact_path.write_bytes(data)
-    return artifact_path
-
+    if int(hash_algo.value(artifact_path.read_bytes()).hexdigest(), 16) != hash_value:
+        # this means either PyPI is hacked or there is a man-in-the-middle
+        raise ImportError("Hashes don't match. Aborting. Something very fishy is going on.")
+    log.info("Download successful.")
+    return
 
-def _pure_python_package(artifact_path, meta):
-    not_pure_python = any(
-        any(n.endswith(s) for s in importlib.machinery.EXTENSION_SUFFIXES) for n in meta["names"]
-    )
 
+@beartype
+def _is_pure_python_package(artifact_path: Path, meta: dict) -> bool:
+    for n, s in product(meta["names"], importlib.machinery.EXTENSION_SUFFIXES):
+        if n.endswith(s):
+            return False
     if ".tar" in str(artifact_path):
         return False
-    if not_pure_python:
-        return False
     return True
 
 
-def _find_module_in_venv(package_name, version, relp):
-    ___use = getattr(sys, "modules").get("use")
-    ret = None, None
-    site_dirs = list(
-        (getattr(___use, "home") / "venv" / package_name / str(version)).rglob("**/site-packages")
-    )
-    if not site_dirs:
-        return ret
-
-    dist = None
-    mod_relative_to_site = None
-    osp = None
-    for site_dir in site_dirs:
-        osp = list(sys.path)
-        sys.path.clear()
-        sys.path.insert(0, str(site_dir))
-        try:
-            dist = importlib.metadata.Distribution.from_name(package_name)
-            while not mod_relative_to_site:
-                pps = [pp for pp in dist.files if pp.as_posix() == relp]
-                if pps:
-                    mod_relative_to_site = pps[0]
-                    break
-                if len(relp.split("/")) == 0:
-                    break
-                relp = "/".join(relp.split("/")[1:])
-        except PackageNotFoundError:
-            continue
-        finally:
-            sys.path.remove(str(site_dir))
-            sys.path += osp
-    if mod_relative_to_site:
-        module_path = site_dir / mod_relative_to_site.as_posix()
-        return (ret := site_dir, module_path)
-    return ret
-
-
-def _find_or_install(name, version=None, artifact_path=None, url=None, out_info=None, force_install=False):
-    if out_info is None:
-        out_info = {}
-    info = out_info
-    package_name, rest = _parse_name(name)
-
-    if isinstance(url, str):
-        url = URL(url)
-    filename = artifact_path.name if artifact_path else None
-    if url:
-        filename = url.asdict()["path"]["segments"][-1]
-    else:
-        filename = artifact_path.name if artifact_path else None
-    if filename and not artifact_path:
-        artifact_path = use.home / "packages" / filename
-
-    if not url or not artifact_path or (artifact_path and not artifact_path.exists()):
-        proj = _get_package_data(package_name)
-        ordered = _filtered_and_ordered_data(proj, version=version)
-        rel = None
-        for r in ordered:
-            rel = r
-            break
-        if not rel:
-            v = list(proj.releases.keys())[0]
-            rel_vals = proj.releases.get(v, [None])
-            rel = rel_vals[0]
-        info.update(rel.dict())
-        out_info.update(rel.dict())
-
-        url = URL(rel.url)
-        filename = url.asdict()["path"]["segments"][-1]
-        artifact_path = use.home / "packages" / filename
-
-    out_info["artifact_path"] = artifact_path
-    # todo: set info
-    as_dict = info
-    url = URL(as_dict["url"])
-    filename = url.path.segments[-1]
-    info["filename"] = filename
-    # info.update(_parse_filename(filename))
-    info = {**info, "version": Version(version)}
-    if not artifact_path.exists():
-        artifact_path = _ensure_path(_download_artifact(name, version, filename, url))
-
-    out_info.update(info)
-    install_item = artifact_path
+@beartype
+def _find_module_in_venv(package_name: str, version: Version, relp: str) -> Path:
+    env_dir = config.venv / package_name / str(version)
+    log.debug("env_dir=%s", env_dir)
+    site_dirs = [
+        env_dir / f"Lib{suffix}" / "site-packages"
+        if sys.platform == "win32"
+        else env_dir / f"lib{suffix}" / ("python%d.%d" % sys.version_info[:2]) / "site-packages"
+        for suffix in ("64", "")
+    ]
+    log.debug("site_dirs=%s", site_dirs)
+    for p in env_dir.glob("**/*"):
+        log.debug("  - %s", p.relative_to(env_dir).as_posix())
+
+    original_sys_path = sys.path
+    try:
+        # Need strings for sys.path to work
+        sys.path = [*map(str, site_dirs), *sys.path]
+        sys.path_importer_cache.clear()
+        importlib.invalidate_caches()
+        # sic! importlib uses sys.path for lookup
+        dist = Distribution.from_name(package_name)
+        log.info("dist=%s", dist)
+        log.debug("dist.files=%s", dist.files)
+        path_set = dist.files
+        log.debug("path_set=%s", path_set)
+        for path in path_set:
+            log.debug("path=%s", path)
+            file = dist.locate_file(path)
+            log.debug("file=%s", file)
+            if not file.exists():
+                continue
+            real_file = Path(*file.parts[: -len(path.parts)])
+            log.debug("real_file=%s", real_file)
+            if real_file.exists():
+                return real_file
+
+    finally:
+        sys.path = original_sys_path
+    raise ImportError("No module in site_dirs")
+
+
+@beartype
+def _install(
+    *,
+    package_name: str,
+    version: Version = None,
+    force_install=False,
+    artifact_path: Path,
+) -> RegistryEntry:
+    """Take care of the installation."""
     meta = archive_meta(artifact_path)
     import_parts = re.split("[\\\\/]", meta["import_relpath"])
     if "__init__.py" in import_parts:
         import_parts.remove("__init__.py")
-    import_name = ".".join(import_parts)
-    name = f"{package_name}.{import_name}"
-    relp = meta["import_relpath"]
-    out_info["module_path"] = relp
-    out_info["import_relpath"] = relp
-    out_info["import_name"] = import_name
-    if (
-        not force_install
-        and _pure_python_package(artifact_path, meta)
-        and str(artifact_path).endswith(".whl")
-    ):
-        return out_info
-
-    venv_root = _venv_root(package_name, version, use.home)
+    relp: str = meta["import_relpath"]
+    venv_root = config.venv / package_name / str(version)
     site_pkgs_dir = list(venv_root.rglob("site-packages"))
     if not any(site_pkgs_dir):
         force_install = True
         module_paths = []
     else:
-        out_info["installation_path"] = site_pkgs_dir[0]
+        installation_path = site_pkgs_dir[0]
         module_paths = venv_root.rglob(f"**/{relp}")
 
     python_exe = Path(sys.executable)
 
     if not module_paths or force_install:
-
         # If we get here, the venv/pip setup is required.
-        output = _process(
+        # we catch errors one level higher, so we don't have to deal with them here
+        env = {}
+        _realenv = {
+            k: v
+            for k, v in chain(os.environ.items(), env.items())
+            if isinstance(k, str) and isinstance(v, str)
+        }
+
+        argv = [
             python_exe,
             "-m",
             "pip",
@@ -680,136 +714,106 @@ def _find_or_install(name, version=None, artifact_path=None, url=None, out_info=
             "--no-warn-script-location",
             "--force-reinstall",
             "--no-warn-conflicts",
-            install_item,
-        )
-    site_dir, module_path = _find_module_in_venv(package_name, version, relp)
-    module_paths = []
-    if module_path:
-        module_paths.append(module_path)
-        installation_path = site_dir
-        site_pkgs_dir = site_dir
-
-    out_info.update(**meta)
-    assert module_paths
-    out_info.update(
-        {
-            **info,
-            "artifact_path": artifact_path,
-            "installation_path": installation_path,
-            "module_path": module_path,
-            "import_relpath": ".".join(relp.split("/")[0:-1]),
-            "info": info,
-        }
+            artifact_path,
+        ]
+        try:
+            setup = dict(
+                executable=python_exe,
+                args=[*map(str, argv)],
+                bufsize=1024,
+                input="",
+                capture_output=False,
+                timeout=45000,
+                check=True,
+                close_fds=True,
+                env=_realenv,
+                encoding="ISO-8859-1",
+                errors="ISO-8859-1",
+                text=True,
+                shell=False,
+            )
+        except CalledProcessError as err:
+            log.error("::".join(err.cmd, err.output, err.stdout, err.stderr))
+            raise InstallationError(err) from err
+        output = run(**setup)
+        log.info("Installation successful.")
+
+    installation_path = _find_module_in_venv(package_name=package_name, version=version, relp=relp)
+
+    return RegistryEntry(
+        installation_path=installation_path,
+        artifact_path=artifact_path,
+        pure_python_package=False,
     )
-    return _delete_none(out_info)
 
 
-def _load_venv_entry(*, package_name, module_name, installation_path, module_path) -> ModuleType:
-    cwd = Path.cwd()
-    orig_exc = None
-    old_sys_path = list(sys.path)
+@beartype
+def _load_venv_entry(*, module_name: str, installation_path: Path) -> ModuleType:
+    if not module_name:
+        log.info("Module name is empty, returning empty Module.")
+        return ModuleType("")
+    origcwd = Path.cwd()
+    original_sys_path = list(sys.path)
+    # TODO we need to keep track of package-specific sys-paths
     if sys.path[0] != "":
         sys.path.insert(0, "")
-    with open(module_path, "rb") as code_file:
-        try:
-            for variant in (
-                cwd,
-                installation_path,
-                Path(str(str(installation_path).replace("lib64/", "lib/"))),
-                Path(str(str(installation_path).replace("lib/", "lib64/"))),
-                installation_path,
-            ):
-                if not variant.exists():
-                    continue
-                origcwd = Path.cwd()
-                try:
-                    log.info("Changimg directory: from %s to %s", origcwd, variant)
-                    os.chdir(variant)
-                    return importlib.import_module(module_name.replace("/", "."))
-                except ImportError as ierr0:
-                    orig_exc = orig_exc or ierr0
-                    continue
-                finally:
-                    log.debug("Change directory back: from %s to %s", Path.cwd(), origcwd)
-                    os.chdir(origcwd)
-        except RuntimeError as ierr:
-            try:
-                return importlib.import_module(module_name)
-            except BaseException as ierr2:
-                raise ierr from orig_exc
-        finally:
-            os.chdir(cwd)
-            sys.path.clear()
-            for p in old_sys_path:
-                sys.path.append(p)
+    try:
+        os.chdir(installation_path)
+        # importlib and sys.path.. bleh
+        return importlib.import_module(module_name)
+    except BaseException as err:
+        msg = err
+        log.error(msg)
+        traceback.print_exc(file=sys.stderr)
+    finally:
+        os.chdir(origcwd)
+        sys.path = original_sys_path
+    raise ImportError(msg)
 
 
 @cache(maxsize=512)
-def _get_package_data(package_name: str) -> PyPI_Project:
-    json_url = f"https://pypi.org/pypi/{package_name}/json"
+def _get_data_from_pypi(*, package_name: str, version: str = None) -> PyPI_Project:
+    if version:
+        json_url = f"https://pypi.org/pypi/{package_name}/{version}/json"
+    else:
+        json_url = f"https://pypi.org/pypi/{package_name}/json"
     response = requests.get(json_url)
     if response.status_code == 404:
-        raise ImportError(Message.pebkac_unsupported(package_name))
+        raise ImportError(UserMessage.pebkac_unsupported(package_name))
     elif response.status_code != 200:
-        raise RuntimeWarning(Message.web_error(json_url, response))
+        raise RuntimeWarning(UserMessage.web_error(json_url, response))
     return PyPI_Project(**response.json())
 
 
-def _filter_by_platform(
-    project: PyPI_Project, tags: frozenset[PlatformTag], sys_version: Version
-) -> PyPI_Project:
-    filtered = {
-        ver: [
-            rel.dict()
-            for rel in releases
-            if _is_compatible(
-                rel,
-                sys_version=sys_version,
-                platform_tags=tags,
-                include_sdist=False,
-            )
-        ]
-        for ver, releases in project.releases.items()
-    }
+@beartype
+def _filter_by_platform(releases: list[PyPI_Release], *, tags: frozenset[PlatformTag]) -> list[PyPI_Release]:
+    filtered = [
+        release for release in releases if _is_compatible(release, platform_tags=tags, include_sdist=False)
+    ]
     if not filtered:
-      filtered = {
-          ver: [
-              rel.dict()
-              for rel in releases
-              if _is_compatible(
-                  rel,
-                  sys_version=sys_version,
-                  platform_tags=tags,
-                  include_sdist=True,
-              )
-          ]
-          for ver, releases in project.releases.items()
-      }
-
-    return PyPI_Project(**{**project.dict(), **{"releases": filtered}})
+        return [
+            release for release in releases if _is_compatible(release, platform_tags=tags, include_sdist=True)
+        ]
+    return filtered
 
 
-@pipes
-def _filtered_and_ordered_data(data: PyPI_Project, version: Version = None) -> list[PyPI_Release]:
-    if version:
-        version = Version(str(version))
-        filtered = (
-            data
-            >> _filter_by_version(version)
-            >> _filter_by_platform(tags=get_supported(), sys_version=sys.version_info[0:2])  # let's not filter by platform for now
-        )
-    else:
-        filtered = data
-        filtered = _filter_by_platform(data, tags=get_supported(), sys_version=sys.version_info[0:2])
+@beartype
+def _get_releases(project: PyPI_Project) -> list[PyPI_Release]:
+    return reduce(list.__add__, project.releases.values(), [])
+
 
-    flat = reduce(list.__add__, filtered.releases.values(), [])
+@beartype
+@pipes
+def _sort_releases(releases: list[PyPI_Release]) -> list[PyPI_Release]:
     return sorted(
-        flat,
-        key=(lambda r: (
-            1 - int(r.filename.endswith(".tar.gz")),
-            1 - int(r.is_sdist),
-            r.version,
-        )),
+        releases,
+        key=(
+            lambda r: (
+                1 - int(r.filename.endswith(".tar.gz")),
+                1 - int(r.is_sdist),
+                r.version,
+            )
+        ),
         reverse=True,
     )
 
@@ -827,6 +831,7 @@ def _is_version_satisfied(specifier: str, sys_version) -> bool:
     return not specifier or sys_version in specifiers
 
 
+@beartype
 @pipes
 def _is_platform_compatible(
     info: PyPI_Release, platform_tags: frozenset[PlatformTag], include_sdist=False
@@ -837,7 +842,7 @@ def _is_platform_compatible(
 
     if "win" in (info.packagetype or "unknown") and sys.platform != "win32":
         return False
-    
+
     if info.platform_tag:
         if "win32" in info.platform_tag and sys.platform != "win32":
             return False
@@ -845,21 +850,20 @@ def _is_platform_compatible(
             return False
 
     our_python_tag = tags.interpreter_name() + tags.interpreter_version()
-    supported_tags = set(
-        [
-            our_python_tag,
-            "py3",
-            "cp3",
-            f"cp{tags.interpreter_version()}",
-            f"py{tags.interpreter_version()}",
-        ]
-    )
+    supported_tags = {
+        our_python_tag,
+        "py3",
+        "cp3",
+        f"cp{tags.interpreter_version()}",
+        f"py{tags.interpreter_version()}",
+    }
+
 
     if info.platform_tag:
         given_platform_tags = info.platform_tag.split(".") << map(PlatformTag) >> frozenset
     else:
         return include_sdist
-    
+
     if info.is_sdist and info.requires_python:
         given_python_tag = {
             our_python_tag
@@ -867,18 +871,15 @@ def _is_platform_compatible(
             if Version(platform.python_version()) in SpecifierSet(p)
         }
     else:
-        given_python_tag = set(info.python_tag.split(".")) 
-    
+        given_python_tag = set(info.python_tag.split("."))
+
     return any(supported_tags.intersection(given_python_tag)) and (
         (info.is_sdist and include_sdist) or any(given_platform_tags.intersection(platform_tags))
     )
 
 
-def _is_compatible(info: PyPI_Release, sys_version, platform_tags, include_sdist=None) -> bool:
-    """Return true if the artifact described by 'info'
-    is compatible with the current or specified system."""
-    specifier = info.requires_python
-
+@beartype
+def _is_compatible(info: PyPI_Release, platform_tags, include_sdist=False) -> bool:
     return (
         _is_platform_compatible(info, platform_tags, include_sdist)
         and not info.yanked
@@ -886,28 +887,7 @@ def _is_compatible(info: PyPI_Release, sys_version, platform_tags, include_sdist
     )
 
 
-def _apply_aspect(
-    thing,
-    check,
-    pattern,
-    decorator,
-    aspectize_dunders=False,
-) -> Any:
-    """Apply the aspect as a side-effect, no copy is created."""
-    for name, obj in thing.__dict__.items():
-        if isinstance(obj, (dict, list, set, tuple, str, bytes)):
-            continue
-        if name == "beartype": continue
-        module = getattr(obj, "__module__", None)
-        if module in ("builtins", "collections", "collections.abc", "functools", "inspect", "itertools", "logging", "pprint", "subprocess", "textwrap", "typing", "warnings",): continue
-        if not aspectize_dunders and name.startswith("__") and name.endswith("__"):
-            continue
-        if check(obj) and re.match(pattern, name):
-            thing.__dict__[name] = decorator(obj)
-            log.debug(f"Applied {decorator.__name__} to {module}::{obj.__qualname__} [{obj.__class__.__qualname__}]")
-    return thing
-
-
+@beartype
 def _get_version(name: Optional[str] = None, package_name=None, /, mod=None) -> Optional[Version]:
     version: Optional[Union[Callable[...], Version, Version, str]] = None
     for lookup_name in (name, package_name):
@@ -926,7 +906,7 @@ def _get_version(name: Optional[str] = None, package_name=None, /, mod=None) ->
         return Version(version)
     version = getattr(mod, "version", version)
     if callable(version):
-        version = version.__call__()
+        version = version()
     if isinstance(version, str):
         return Version(version)
     return Version(version)
@@ -934,20 +914,23 @@ def _get_version(name: Optional[str] = None, package_name=None, /, mod=None) ->
 
 def _build_mod(
     *,
-    name,  # TODO: this should be a package name and module name
+    module_name,
     code,
     initial_globals: Optional[dict[str, Any]],
     module_path,
+    package_name="",
 ) -> ModuleType:
-
-    package_name, rest = _parse_name(name)
-    mod = ModuleType(rest)
-
+    mod = ModuleType(module_name)
+    log.info(f"{Path.cwd()=} {package_name=} {module_name=} {module_path=}")
     mod.__dict__.update(initial_globals or {})
     mod.__file__ = str(module_path)
     mod.__path__ = [str(module_path.parent)]
     mod.__package__ = package_name
-    mod.__name__ = rest
+    mod.__name__ = module_name
+    loader = SourceFileLoader(module_name, str(module_path))
+    mod.__loader__ = loader
+    mod.__spec__ = ModuleSpec(module_name, loader)
+    sys.modules[module_name] = mod
     code_text = codecs.decode(code)
     # module file "<", ">" chars are specially handled by inspect
     getattr(linecache, "cache")[module_path] = (
@@ -969,6 +952,24 @@ def _build_mod(
 
 def _fail_or_default(exception: BaseException, default: Any):
     if default is not Modes.fastfail:
-        return default  # TODO: write test for default
+        return default
     else:
-        raise exception
\ No newline at end of file
+        raise exception
+
+
+# ### active web dev ###
+# proj = None
+# from shutil import copy
+
+
+# def _qwer():
+#     copy(Path(__file__).absolute().parent / r"templates/stylesheet.css", config.home / "stylesheet.css")
+#     package_name = "pygame"
+#     name = "pygame/foo"
+#     version = Version("2.1.0")
+#     global proj
+#     if proj is None:
+#         proj = _get_data_from_pypi(package_name=package_name, version=version)
+#         proj = _filter_by_version(proj, version)
+
+#     _web_pebkac_no_hash(package_name=package_name, version=version, name=name, project=proj)
diff --git a/src/use/pypi_model.py b/src/use/pydantics.py
old mode 100755
new mode 100644
similarity index 66%
rename from src/use/pypi_model.py
rename to src/use/pydantics.py
index e749c867..44500dec
--- a/src/use/pypi_model.py
+++ b/src/use/pydantics.py
@@ -1,26 +1,49 @@
 """
 Pydantic model for the PyPI JSON API.
 """
-from __future__ import annotations
-
+import os
 import re
+from logging import getLogger
 from pathlib import Path
 from typing import Optional, Union
 
 import packaging
 from packaging.version import Version as PkgVersion
 from pydantic import BaseModel
-from logging import getLogger
 
 log = getLogger(__name__)
 
+
 # Well, apparently they refuse to make Version iterable, so we'll have to do it ourselves.
 # This is necessary to compare sys.version_info with Version and make some tests more elegant, amongst other things.
 
+
 class BaseModel(BaseModel):
     def __repr__(self):
         return self.__class__.__name__
 
+
+class Configuration(BaseModel):
+    version_warning: bool = True
+    no_browser: bool = False
+    disable_jack: bool = bool(int(os.getenv("DISABLE_JACK", "0")))
+    debugging: bool = bool(int(os.getenv("DEBUG", "0")))
+    use_db: bool = bool(int(os.getenv("USE_DB", "1")))
+    testing: bool = bool(int(os.getenv("TESTING", "0")))
+    home: Path = Path(os.getenv("JUSTUSE_HOME", str(Path.home() / ".justuse-python"))).absolute()
+    venv: Path = Path(os.getenv("JUSTUSE_HOME", str(Path.home() / ".justuse-python"))).absolute() / "venv"
+    packages: Path = (
+        Path(os.getenv("JUSTUSE_HOME", str(Path.home() / ".justuse-python"))).absolute() / "packages"
+    )
+    logs: Path = Path(os.getenv("JUSTUSE_HOME", str(Path.home() / ".justuse-python"))).absolute() / "logs"
+    registry: Path = (
+        Path(os.getenv("JUSTUSE_HOME", str(Path.home() / ".justuse-python"))).absolute() / "registry.db"
+    )
+
+    class Config:
+        validate_assignment = True
+
+
 class Version(PkgVersion):
     def __new__(cls, *args, **kwargs):
         if args and isinstance(args[0], Version):
@@ -71,6 +94,15 @@ def _delete_none(a_dict: dict[str, object]) -> dict[str, object]:
     return a_dict
 
 
+class RegistryEntry(BaseModel):
+    class Config:
+        validate_assignment = True
+
+    artifact_path: Path
+    installation_path: Path
+    pure_python_package: bool
+
+
 class JustUse_Info(BaseModel):
     distribution: Optional[str]
     version: Optional[str]
@@ -82,27 +114,23 @@ class JustUse_Info(BaseModel):
 
 
 class PyPI_Release(BaseModel):
+    abi_tag: Optional[str]
+    build_tag: Optional[str]
+    distribution: Optional[str]
     digests: dict[str, str]
-    url: str
-    packagetype: str
-    distribution: str
+    ext: Optional[str]
+    filename: str
     requires_python: Optional[str]
+    packagetype: str
+    platform_tag: Optional[str]
     python_version: Optional[str]
     python_tag: Optional[str]
-    platform_tag: str
-    filename: str
-    abi_tag: str
-    yanked: bool
+    url: str
     version: Version
-    distribution: Optional[str]
-    build_tag: Optional[str]
-    python_tag: Optional[str]
-    abi_tag: Optional[str]
-    platform_tag: Optional[str]
-    ext: Optional[str]
+    yanked: bool
 
     class Config:
-        arbitrary_types_allowed = True
+        validate_assignment = True
 
     @property
     def is_sdist(self):
@@ -117,15 +145,14 @@ def justuse(self) -> JustUse_Info:
             ext = self.filename[self.filename.index(".tar") + 1 :]
         else:
             ext = pp.name[len(pp.stem) + 1 :]
-        rest = pp.name[0 : -len(ext) - 1]
+        rest = pp.name[: -len(ext) - 1]
 
         not_dash = lambda name: f"(?P<{name}>[^-]+)"
         not_dash_with_int = lambda name: f"(?P<{name}>[0-9][^-]*)"
-        match = re.match(
+        if match := re.match(
             f"{not_dash('distribution')}-{not_dash('version')}-?{not_dash_with_int('build_tag')}?-?{not_dash('python_tag')}?-?{not_dash('abi_tag')}?-?{not_dash('platform_tag')}?",
             rest,
-        )
-        if match:
+        ):
             return JustUse_Info(**_delete_none(match.groupdict()), ext=ext)
         return JustUse_Info()
 
@@ -137,21 +164,11 @@ class PyPI_Downloads(BaseModel):
 
 
 class PyPI_Info(BaseModel):
-    author: Optional[str]
-    author_email: Optional[str]
-    bugtrack_url: Optional[str]
-    classifiers: Optional[list[str]]
-    description: Optional[str]
+    class Config:
+        extra = "ignore"
+
     description_content_type: Optional[str]
-    docs_url: Optional[str]
     download_url: Optional[str]
-    downloads: Optional[PyPI_Downloads]
-    home_page: Optional[str]
-    keywords: Optional[str]
-    license: Optional[str]
-    maintainer: Optional[str]
-    maintainer_email: Optional[str]
-    name: str
     package_name: Optional[str]
     package_url: str
     platform: Optional[str]
@@ -188,52 +205,53 @@ class PyPI_URL(BaseModel):
 
 
 class PyPI_Project(BaseModel):
-    releases: dict[Version, list[PyPI_Release]] = None
+    releases: dict[Version, list[PyPI_Release]] = {}
     urls: list[PyPI_URL] = None
     last_serial: int = None
     info: PyPI_Info = None
 
+    class Config:
+        extra = "ignore"
+
     def __init__(self, *, releases, urls, info, **kwargs):
-      try:
-        for version in list(releases.keys()):
-            if not isinstance(version, str):
-                continue
-            try:
-                Version(version)
-            except packaging.version.InvalidVersion:
-                del releases[version]
-
-        def get_info(rel_info, ver_str):
-           data = {
+        try:
+            for version in list(releases.keys()):
+                if not isinstance(version, str):
+                    continue
+                try:
+                    Version(version)
+                except packaging.version.InvalidVersion:
+                    del releases[version]
+
+            def get_info(rel_info, ver_str):
+                data = {
                     **rel_info,
                     **_parse_filename(rel_info["filename"]),
                     "version": Version(str(ver_str)),
                 }
-           if info.get("requires_python"): data["requires_python"] = info.get("requites_python")
-           if info.get("requires_dist"): data["requires_dist"] = info.get("requires_dist")
-           return data
-        
-        super(PyPI_Project, self).__init__(
-          releases={
-            str(ver_str): [
-                get_info(rel_info, ver_str)
-                for rel_info in release_infos
-            ]
-            for ver_str, release_infos in releases.items()
-          },
-          urls=[
-            get_info(rel_info, ver_str) 
-            for ver_str, rel_infos in releases.items()
-            for rel_info in rel_infos
-          ],
-          info=info,
-          **kwargs
-        )
-      finally:
-        releases = None
-        info = None
-        urls = None
-        
+                if info.get("requires_python"):
+                    data["requires_python"] = info.get("requites_python")
+                if info.get("requires_dist"):
+                    data["requires_dist"] = info.get("requires_dist")
+                return data
+
+            super(PyPI_Project, self).__init__(
+                releases={
+                    str(ver_str): [get_info(rel_info, ver_str) for rel_info in release_infos]
+                    for ver_str, release_infos in releases.items()
+                },
+                urls=[
+                    get_info(rel_info, ver_str)
+                    for ver_str, rel_infos in releases.items()
+                    for rel_info in rel_infos
+                ],
+                info=info,
+                **kwargs,
+            )
+        finally:
+            releases = None
+            info = None
+            urls = None
 
 
 def _parse_filename(filename) -> dict:
@@ -254,32 +272,31 @@ def _parse_filename(filename) -> dict:
     else:
         ext = pp.name[len(pp.stem) + 1 :]
         packagetype = "bdist"
-    rest = pp.name[0 : -len(ext) - 1]
+    rest = pp.name[: -len(ext) - 1]
 
     p = rest.split("-")
     np = len(p)
-    if np == 5:
+    if np == 2:
+        distribution, version = p
+    elif np == 3:
+        distribution, version, python_tag = p
+    elif np == 5:
         distribution, version, python_tag, abi_tag, platform_tag = p
     elif np == 6:
         distribution, version, build_tag, python_tag, abi_tag, platform_tag = p
-    elif np == 3:  # ['SQLAlchemy', '0.1.1', 'py2.4']
-        distribution, version, python_tag = p
-    elif np == 2:
-        distribution, version = p
     else:
         return {}
-    
+
     return {
-            "distribution": distribution,
-            "version": version,
-            "build_tag": build_tag,
-            "python_tag": python_tag,
-            "abi_tag": abi_tag,
-            "platform_tag": platform_tag,
-            "ext": ext,
-            "filename": filename,
-            "packagetype": packagetype,
-            "yanked_reason": "",
-            "bugtrack_url": "",
-            
+        "distribution": distribution,
+        "version": version,
+        "build_tag": build_tag,
+        "python_tag": python_tag,
+        "abi_tag": abi_tag,
+        "platform_tag": platform_tag,
+        "ext": ext,
+        "filename": filename,
+        "packagetype": packagetype,
+        "yanked_reason": "",
+        "bugtrack_url": "",
     }
diff --git a/src/use/templates/aspects.css b/src/use/templates/aspects.css
new file mode 100644
index 00000000..2c3c57eb
--- /dev/null
+++ b/src/use/templates/aspects.css
@@ -0,0 +1,170 @@
+
+
+body {
+    font-family: Arial Unicode MS, Lucida Sans Unicode, DejaVu Sans, Quivira, Symbola, Code2000;
+    padding: 30px;
+    line-height: 1.6;
+    background:#131313;
+    color:#fff;
+}
+
+* {
+    box-sizing: border-box;
+    }
+
+h1 {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin: 0;
+    animation: glitch 5s linear infinite alternate-reverse;
+    color: #50BFE6;
+}
+
+@keyframes glitch{
+    2%,64%{
+      transform: translate(2px,0) skew(50deg);
+      opacity: 0;
+      text-shadow: 0 0 50px #50BFE6;
+    }
+    4%,60%{
+      transform: translate(-2px,0) skew(-10deg);
+      opacity: 0.7;
+      text-shadow: 0 0 20px #50BFE6;
+    }
+    62%{
+      transform: translate(0,0) skew(0deg); 
+      opacity: 1;
+      text-shadow: 0 0 10px #50BFE6;
+    }
+    80%{
+      opacity: 1;
+      text-shadow: 0 0 5px #50BFE6;
+    }
+  }
+
+h1:before,
+h1:after{
+  content: attr(title);
+  position: absolute;
+  left: 0;
+}
+
+h1:before{
+  animation: glitchTop 1s linear infinite;
+  clip-path: polygon(0 0, 100% 0, 100% 33%, 0 33%);
+  -webkit-clip-path: polygon(0 0, 100% 0, 100% 33%, 0 33%);
+}
+
+@keyframes glitchTop{
+  2%,64%{
+    transform: translate(2px,-2px);
+  }
+  4%,60%{
+    transform: translate(-2px,2px);
+  }
+  62%{
+    transform: translate(13px,-1px) skew(-13deg); 
+  }
+}
+
+h1:after{
+  animation: glitchBottom 1.5s linear infinite;
+  clip-path: polygon(0 67%, 100% 67%, 100% 100%, 0 100%);
+  -webkit-clip-path: polygon(0 67%, 100% 67%, 100% 100%, 0 100%);
+}
+
+@keyframes glitchBottom{
+  2%,64%{
+    transform: translate(-2px,0);
+  }
+  4%,60%{
+    transform: translate(-2px,0);
+  }
+  62%{
+    transform: translate(-22px,5px) skew(21deg); 
+  }
+}
+
+section {
+position: relative;
+margin: 0 auto;
+z-index: 0;
+color: white;
+}
+
+
+
+th {
+    border-bottom:1px solid black;}
+
+tr {border: 1px  white; }
+
+p {
+font-size: 1em;
+}
+
+div {
+display: inline-block;
+}
+
+div:focus {
+outline: none;
+}
+
+body {
+padding: 3em;
+}
+
+.p {
+margin-top: 300px;
+text-align: center;
+display: block;
+font-size: 1em;
+}
+
+.filter_field > button {
+  vertical-align: middle;
+}
+.filter_field > textarea {
+vertical-align: middle;
+}
+
+table {
+  left: 0;
+  top: 38px;
+  width: 100%;
+  font-size: 1em;
+  text-align: left;
+  background: rgb(1, 18, 114);
+  padding: 40px 0;
+  border-collapse: collapse;
+  color: white;
+  font-weight: lighter;
+  }
+
+.selected {
+  padding: 5px;
+  border-bottom: gray solid 2px;
+  border-top: white solid 2px;
+  background: #b5bfcf;
+  box-shadow: inset 1px 1px 3px #000;
+  color: rgb(51, 39, 39);
+  font-weight: bold;
+
+}
+.failure {
+  background: red;
+  }
+
+  .visible {
+    visibility: visible;
+    opacity: 1;
+    transition: opacity 2s linear;
+  }
+
+  .hidden {
+    visibility: hidden;
+    opacity: 0;
+    transition: visibility 0s 2s, opacity 2s linear;
+  }
\ No newline at end of file
diff --git a/src/use/templates/aspects.html b/src/use/templates/aspects.html
new file mode 100644
index 00000000..e9d09c6f
--- /dev/null
+++ b/src/use/templates/aspects.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="aspects.css">
+
+    <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
+    <script>
+        mermaid.initialize({ startOnLoad: true });
+    </script>
+    <script src="https://cdn.jsdelivr.net/npm/brython@3.10/brython.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/brython@3.10/brython_stdlib.js"></script>
+    <title>Justuse</title>
+</head>
+
+<body onload="brython()">
+    <header>
+        <h1>Applied Aspects</h1>
+    </header>
+    <p> Here is a list of objects we have decorated, in order of application.</p>
+
+    <section>
+        <table id="decorators">
+            <tr>
+                <th> name</th>
+                <th> decorators </th>
+            </tr>
+            {% for entry in decorators %}
+            <tr id="deco_row_{{loop.index}}">
+                <td> {{ entry.name }} </td>
+                <td> {{ entry.func}} </td>
+            </tr>
+            {% endfor %}
+        </table>
+    </section>
+    <!-- 
+    <section>
+
+        <table id="functions">
+            <tr>
+                <th> name</th>
+                <th> decorators </th>
+            </tr>
+            {% for entry in functions %}
+            <tr id="deco_row_{{loop.index}}">
+                <td> {{ entry.name }} </td>
+                <td> {{ entry.func}} </td>
+            </tr>
+            {% endfor %}
+        </table>
+    </section> -->
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/use/templates/aspects_dry_run.html b/src/use/templates/aspects_dry_run.html
new file mode 100644
index 00000000..175b4fca
--- /dev/null
+++ b/src/use/templates/aspects_dry_run.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="aspects.css">
+
+    <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
+    <script>
+        mermaid.initialize({ startOnLoad: true });
+    </script>
+    <script src="https://cdn.jsdelivr.net/npm/brython@3.10/brython.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/brython@3.10/brython_stdlib.js"></script>
+    <title>Justuse</title>
+</head>
+
+<body onload="brython()">
+    <header>
+        <div id="decorator" style="display:none">{{decorator.__name__}}</div>
+        <div id="module_name" style="display:none">{{module_name}}</div>
+        <h1>Dry Run for applying {{decorator.__name__}} to &lt;{{module_name}}&gt;
+        </h1>
+    </header>
+    <p> Here are all the objects we could decorate inside module/package &lt;{{module_name}}&gt;.</p>
+    <p>
+        <button class="button" , id="clipboard_button">Copy to Clipboard</button>
+    </p>
+    <p>
+        <textarea id="result" rows="5" style="resize: none;" readonly cols="120"></textarea>
+    </p>
+
+    <p>Options for filtering</p>
+
+    <p>
+
+        <label>
+            <input type="checkbox" id="aspectize_dunders">
+            <span class="button" for="aspectize_dunders" id="aspectize_dunders_button">aspectize dunders</span>
+        </label>
+    <p class="filter_field">
+        <textarea id="pattern" rows="2" style="resize: none;" cols="80"></textarea>
+        <em><a href="https://regex101.com/">check this out if you need help building the pattern</a></em>
+    </p>
+
+    <p style="display: inline-block; vertical-align: middle;">
+        <button class=" button" , id="apply_filter_button">Apply Filters</button>
+        <em>
+            <div id="apply_filter_warning" class="hidden">warning</div>
+        </em>
+    </p>
+
+    <p> <label>
+            <input type="checkbox" id="hide_failures" checked>
+            <span class="button" for="hide_failures" id="show_failures_button">Hide non-decoratable objects</span>
+        </label>
+    </p>
+
+    <table id="decorators">
+        <tr>
+            <th> qualname </th>
+            <th> name</th>
+            <th> type </th>
+            <th id="applicable_header" style="display:none;"> applicable </th>
+            <th id="reason_header" style="display:none;"> reason </th>
+        </tr>
+        {% for entry in hits %}
+        <tr id="row_{{loop.index}}" {% if not entry.success %} style="display:none;" {% endif %}
+            class="{% if entry.success %}success{% else %}failure{% endif %} {% if entry.dunder %}selected{% endif %}">
+            <td> {{ entry.qualname }} </td>
+            <td> {{ entry.name }} </td>
+            <td> {{ entry.type.__name__ }} </td>
+            <td class="applicable" style="display:none;"> {{ entry.success }} </td>
+            <td class="reason" style="display:none;"> {{ entry.exception }} </td>
+        </tr>
+        {% endfor %}
+    </table>
+    </section>
+
+    <script type="text/python" id="script">
+from browser import console, document, window, alert, bind, html
+from pprint import  pformat
+import re
+
+document["clipboard_button"].bind("click", lambda event: window.navigator.clipboard.writeText(document["result"].text))
+
+args = {"pattern": document["pattern"].value, "aspectize_dunders": False}
+
+{% raw %}
+result_str = f"""use.apply_aspect({document["module_name"].text}, {document["decorator"].text}, 
+**{pformat(args)})"""
+{% endraw %}
+document["result"].text = result_str
+
+
+@bind(document["hide_failures"], "click")
+def show_failures_button_clicked(event):
+    if document["hide_failures"].checked:
+        for x in document.select(".failure"): 
+            x.style.display = "none"
+        document["applicable_header"].style.display = "none"
+        document["reason_header"].style.display = "none"
+        for x in document.select(".applicable"): 
+            x.style.display = "none"
+        for x in document.select(".reason"): 
+            x.style.display = "none"
+        
+    else:
+        for x in document.select(".failure"): 
+            x.style.display = "table-row"
+
+        document["applicable_header"].style.display = "table-cell"
+        document["reason_header"].style.display = "table-cell"
+        for x in document.select(".applicable"): 
+            x.style.display = "table-cell"
+        for x in document.select(".reason"):
+            x.style.display = "table-cell"
+
+def show_warning(content):
+    document["apply_filter_warning"].textContent = content
+    document["apply_filter_warning"].style.display = "inline-block"
+    document["apply_filter_warning"].class_name = "visible"
+
+@bind(document["apply_filter_button"], "click")
+def apply_filter(event):
+    document["apply_filter_warning"].addEventListener("transitionend", do_filter);
+    show_warning(f"""Working.. (applying filter to {len(document.querySelectorAll('[id^="row_"]'))} objects)""")
+    
+def do_filter(*args):
+    aspectize_dunders = document["aspectize_dunders"].checked
+    pattern = re.compile(document["pattern"].value, re.DOTALL)
+
+    for x in document.querySelectorAll('[id^="row_"]'):
+        name = x.children[1].text
+        selected = False
+        classes = set(x.className.split(" "))
+
+        if pattern.match(name):
+            selected = True 
+
+        if not aspectize_dunders:
+            if name.startswith("__") and name.endswith("__"):
+                selected = False
+
+        if selected:
+            classes |= {"selected"}
+        else:
+            classes -= {"selected"}
+        x.class_name = ' '.join(c for c in classes)
+        document["apply_filter_warning"].class_name = "hidden"
+
+    args = {"pattern": document["pattern"].value, "aspectize_dunders": aspectize_dunders}
+
+    {% raw %}
+    result_str = f"""use.apply_aspect({document["module_name"].text}, {document["decorator"].text}, 
+    **{pformat(args)})"""
+    {% endraw %}
+    document["result"].text = result_str
+    </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/use/templates/hash-presentation.html b/src/use/templates/hash-presentation.html
new file mode 100644
index 00000000..6d2e5006
--- /dev/null
+++ b/src/use/templates/hash-presentation.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="stylesheet.css">
+
+    <script src="https://cdn.jsdelivr.net/npm/brython@3.10/brython.min.js">
+    </script>
+    <script src="https://cdn.jsdelivr.net/npm/brython@3.10/brython_stdlib.js">
+    </script>
+
+
+    <title>Justuse</title>
+</head>
+
+<body onload="brython()">
+    <header>
+        <h1>Auto-installation of {{package_name}} version {{version}}</h1>
+        <div id="name" style="display:none">{{name}}</div>
+        <div id="version" style="display:none">{{version}}</div>
+        Please specify the hash corresponding to the platform you want to run your code on and the python version you
+        want to run your code with.
+        Values from different hash algorithms can't be mixed together, but regular hexdigests and JACK
+        representations are interchangeable. After you selected everything you want, copy & paste the snippet below into
+        your code.
+    </header>
+
+    <p>
+        <button class="button" , id="clipboard_button">Copy to Clipboard</button>
+    </p>
+    <p>
+        <textarea id="result" rows="5" style="resize: none;" readonly cols="120">use('{{name}}', version='{{version}}', modes=use.auto_install, hash_algo=use.Hash.sha256, hashes={ 
+                ... 
+            })</textarea>
+    </p>
+    <p>
+        <label>
+            <input type="checkbox" id="jack_button">
+            <span class="button" for="jack_button" id="jack_button_label">Hash as hexdigest</span>
+        </label>
+
+
+    </p>
+    {% for hash_name in table.keys()%}
+    <section>
+        <div>
+            <button>{{hash_name}}</button>
+        </div>
+        <table id="{{hash_name}}">
+            <tr>
+                <th> Python</th>
+                <th> Platform </th>
+                <th id="hash_header"> Hash </th>
+                <th id="jack_header" style="display:none;"> JACK </th>
+            </tr>
+            {% for entry in table[hash_name] %}
+            <tr id="row_{{loop.index}}" class="row_not_selected">
+                <td style="display:none;"><input type="checkbox" , id="row_selected_{{loop.index}}"></td>
+                <td id=python_row_{{loop.index}}> {{ entry.python }} </td>
+                <td id=platform_row_{{loop.index}}> {{ entry.platform }} </td>
+                <td id="hash_value_row_{{loop.index}}"> {{ entry.hash_value}} </td>
+                <td id="jack_value_row_{{loop.index}}" style="display:none;"> {{ entry.jack_value}} </td>
+            </tr>
+            {% endfor %}
+        </table>
+    </section>
+    {% endfor %}
+
+    <script type="text/python" id="script">
+from browser import console, document, window, alert, bind, html
+
+document["clipboard_button"].bind("click", lambda event: window.navigator.clipboard.writeText(document["result"].text))
+
+@bind(document["jack_button"], "click")
+def jack_button_clicked(event):
+    if event.target.checked:
+        document["jack_button_label"].text = "Hash as JACK"
+        document["hash_header"].style.display = "none"
+        document["jack_header"].style.display = "table-cell"
+        for i, row in enumerate(document.select("tr")):
+            if i == 0: continue
+            document[f"hash_value_row_{i}"].style.display = "none"
+            document[f"jack_value_row_{i}"].style.display = "table-cell"
+    else:
+        document["jack_button_label"].text = "Hash as hexdigest"
+        document["hash_header"].style.display = "table-cell"
+        document["jack_header"].style.display = "none"
+        for i, row in enumerate(document.select("tr")):
+            if i == 0: continue
+            document[f"hash_value_row_{i}"].style.display = "table-cell"
+            document[f"jack_value_row_{i}"].style.display = "none"
+    refresh_result()
+
+def table_clicked_select(event):
+    event.target.parentElement.className = "row_selected"
+    document[event.target.parentElement.id].bind("click", table_clicked_unselect)
+    refresh_result()
+
+
+def table_clicked_unselect(event):
+    event.target.parentElement.className = "row_not_selected"
+    document[event.target.parentElement.id].bind("click", table_clicked_select)
+    refresh_result()
+
+def refresh_result():
+    hashes = set()
+    for row in document.select(".row_selected"):
+        if document["jack_button"].checked: 
+            hashes.add((document[f"python_{row.id}"].text, document[f"platform_{row.id}"].text, document[f"jack_value_{row.id}"].text))
+        else:
+            hashes.add((document[f"python_{row.id}"].text, document[f"platform_{row.id}"].text, document[f"hash_value_{row.id}"].text))
+
+    hash_str = "{\n    " + "\n    ".join(f"'{H}',  # {python}-{platform}" for python, platform, H in hashes) + " \n}"
+    document["result"].text =  f"use('{document['name'].text}', version='{document['version'].text}', modes=use.auto_install, hash_algo=use.Hash.sha256, hashes={hash_str})"
+
+for x in document.select(".row_not_selected"): x.bind("click", table_clicked_select)
+</script>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/use/templates/profiling.css b/src/use/templates/profiling.css
new file mode 100644
index 00000000..be8f1694
--- /dev/null
+++ b/src/use/templates/profiling.css
@@ -0,0 +1,134 @@
+
+
+body {
+    font-family: Arial Unicode MS, Lucida Sans Unicode, DejaVu Sans, Quivira, Symbola, Code2000;
+    padding: 30px;
+    line-height: 1.6;
+    background:#131313;
+    color:#fff;
+}
+
+* {
+    box-sizing: border-box;
+    }
+
+h1 {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin: 0;
+    animation: glitch 5s linear infinite alternate-reverse;
+    color: #50BFE6;
+}
+
+@keyframes glitch{
+    2%,64%{
+      transform: translate(2px,0) skew(50deg);
+      opacity: 0;
+      text-shadow: 0 0 50px #50BFE6;
+    }
+    4%,60%{
+      transform: translate(-2px,0) skew(-10deg);
+      opacity: 0.7;
+      text-shadow: 0 0 20px #50BFE6;
+    }
+    62%{
+      transform: translate(0,0) skew(0deg); 
+      opacity: 1;
+      text-shadow: 0 0 10px #50BFE6;
+    }
+    80%{
+      opacity: 1;
+      text-shadow: 0 0 5px #50BFE6;
+    }
+  }
+
+h1:before,
+h1:after{
+  content: attr(title);
+  position: absolute;
+  left: 0;
+}
+
+h1:before{
+  animation: glitchTop 1s linear infinite;
+  clip-path: polygon(0 0, 100% 0, 100% 33%, 0 33%);
+  -webkit-clip-path: polygon(0 0, 100% 0, 100% 33%, 0 33%);
+}
+
+@keyframes glitchTop{
+  2%,64%{
+    transform: translate(2px,-2px);
+  }
+  4%,60%{
+    transform: translate(-2px,2px);
+  }
+  62%{
+    transform: translate(13px,-1px) skew(-13deg); 
+  }
+}
+
+h1:after{
+  animation: glitchBottom 1.5s linear infinite;
+  clip-path: polygon(0 67%, 100% 67%, 100% 100%, 0 100%);
+  -webkit-clip-path: polygon(0 67%, 100% 67%, 100% 100%, 0 100%);
+}
+
+@keyframes glitchBottom{
+  2%,64%{
+    transform: translate(-2px,0);
+  }
+  4%,60%{
+    transform: translate(-2px,0);
+  }
+  62%{
+    transform: translate(-22px,5px) skew(21deg); 
+  }
+}
+
+section {
+position: relative;
+margin: 0 auto;
+z-index: 0;
+color: white;
+}
+
+
+
+th {
+    border-bottom:1px solid black;}
+
+tr {border: 1px  white; }
+
+p {
+font-size: 1em;
+}
+
+div {
+display: inline-block;
+}
+
+
+body {
+padding: 3em;
+}
+
+.p {
+margin-top: 300px;
+text-align: center;
+display: block;
+font-size: 1em;
+}
+
+
+table {
+    color: black;
+    left: 0;
+    top: 38px;
+    width: 100%;
+    font-size: 1em;
+    text-align: left;
+    background-color: #93beff;
+    padding: 40px 0;
+    border-collapse: collapse;
+    }
\ No newline at end of file
diff --git a/src/use/templates/profiling.html b/src/use/templates/profiling.html
new file mode 100644
index 00000000..4830cf7c
--- /dev/null
+++ b/src/use/templates/profiling.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="profiling.css">
+
+    <script src="https://cdn.jsdelivr.net/npm/brython@3.10/brython.min.js">
+    </script>
+    <script src="https://cdn.jsdelivr.net/npm/brython@3.10/brython_stdlib.js">
+    </script>
+
+
+    <title>Justuse</title>
+</head>
+
+<body onload="brython()">
+    <header>
+        <p>
+        <h1>Tinny Profiler Statistics</h1>
+        </p>
+    </header>
+    <p> These are the statistics of the tinny profiler gathered since applying the decorator.</p>
+
+    <p>
+    <table>
+        <tr>
+            <th> QualName</th>
+            <th> Name </th>
+            <th> Number of calls </th>
+            <th> Total time spent </th>
+            <th> Minimum </th>
+            <th> Geometric Mean </th>
+            <th> Median </th>
+            <th> Standard Deviation </th>
+        </tr>
+        {% for entry in timings %}
+        <tr id="row_{{loop.index}}">
+            <td>{{entry.qualname}}</td>
+            <td>{{entry.name}}</td>
+            <td>{{entry.len}}</td>
+            <td>{{ (entry.total / 10**9)|round(5) }} sec</td>
+            <td>{{ entry.min }} ns ({{ (entry.min / 10**9)|round(5)}} sec)</td>
+            <td>{{ entry.geom_mean|int }} ns ({{ (entry.geom_mean / 10**9)|round(5) }} sec)</td>
+            <td>{{ entry.median }} ns ({{ (entry.median/ 10**9)|round(5) }} sec)</td>
+            <td>{{ entry.stdev|int}} ns ({{(entry.stdev / 10**9)|round(5)}} sec)</td>
+        </tr>
+        {% endfor %}
+    </table>
+    </p>
+
+    <script type="text/python" id="script">
+from browser import console, document, window, alert, bind, html
+
+
+</script>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/use/templates/stylesheet.css b/src/use/templates/stylesheet.css
new file mode 100644
index 00000000..759311db
--- /dev/null
+++ b/src/use/templates/stylesheet.css
@@ -0,0 +1,192 @@
+
+
+body {
+    font-family: Arial Unicode MS, Lucida Sans Unicode, DejaVu Sans, Quivira, Symbola, Code2000;
+    padding: 30px;
+    line-height: 1.6;
+    background:#131313;
+    color:#fff;
+}
+
+* {
+    box-sizing: border-box;
+    }
+
+h1 {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin: 0;
+    animation: glitch 5s linear infinite alternate-reverse;
+    color: #50BFE6;
+}
+
+@keyframes glitch{
+    2%,64%{
+      transform: translate(2px,0) skew(50deg);
+      opacity: 0;
+      text-shadow: 0 0 50px #50BFE6;
+    }
+    4%,60%{
+      transform: translate(-2px,0) skew(-10deg);
+      opacity: 0.7;
+      text-shadow: 0 0 20px #50BFE6;
+    }
+    62%{
+      transform: translate(0,0) skew(0deg); 
+      opacity: 1;
+      text-shadow: 0 0 10px #50BFE6;
+    }
+    80%{
+      opacity: 1;
+      text-shadow: 0 0 5px #50BFE6;
+    }
+  }
+
+h1:before,
+h1:after{
+  content: attr(title);
+  position: absolute;
+  left: 0;
+}
+
+h1:before{
+  animation: glitchTop 1s linear infinite;
+  clip-path: polygon(0 0, 100% 0, 100% 33%, 0 33%);
+  -webkit-clip-path: polygon(0 0, 100% 0, 100% 33%, 0 33%);
+}
+
+@keyframes glitchTop{
+  2%,64%{
+    transform: translate(2px,-2px);
+  }
+  4%,60%{
+    transform: translate(-2px,2px);
+  }
+  62%{
+    transform: translate(13px,-1px) skew(-13deg); 
+  }
+}
+
+h1:after{
+  animation: glitchBottom 1.5s linear infinite;
+  clip-path: polygon(0 67%, 100% 67%, 100% 100%, 0 100%);
+  -webkit-clip-path: polygon(0 67%, 100% 67%, 100% 100%, 0 100%);
+}
+
+@keyframes glitchBottom{
+  2%,64%{
+    transform: translate(-2px,0);
+  }
+  4%,60%{
+    transform: translate(-2px,0);
+  }
+  62%{
+    transform: translate(-22px,5px) skew(21deg); 
+  }
+}
+
+section {
+position: relative;
+margin: 0 auto;
+z-index: 0;
+color: white;
+}
+
+table {
+color: black;
+position: absolute;
+left: 0;
+top: 38px;
+width: 100%;
+font-size: 1em;
+text-align: left;
+background-color: #93beff;
+padding: 40px 0;
+border-collapse: collapse;
+}
+
+th {
+    border-bottom:1px solid black;}
+
+tr {border: 1px  white; }
+
+button {
+cursor: pointer;
+width: 199px;
+display: inline-block;
+background-color: #5E6B7F;
+color: white;
+text-align: center;
+transition: .25s ease;
+border: none;
+padding: 10px;
+border-radius: 12px 12px 0 0;
+}
+
+p {
+font-size: 1em;
+}
+
+div {
+display: inline-block;
+}
+
+div:focus {
+outline: none;
+}
+
+body {
+padding: 3em;
+}
+
+.p {
+margin-top: 300px;
+text-align: center;
+display: block;
+font-size: 1em;
+}
+
+.button {
+    cursor: pointer;
+    width: 199px;
+    display: inline-block;
+    text-align: center;
+    transition: .25s ease;
+    border-style: outset;
+    border-radius: 12px 12px 12px 12px;
+    padding: 10px;
+    color: white;
+    box-shadow: 0 5px #666;
+}
+
+#clipboard_button {
+    background-color: #5c0847;
+}
+
+#clipboard_button:active {
+    box-shadow: none;
+    transform: translateY(4px);
+}
+
+#jack_button {
+    appearance: none;
+}
+
+#jack_button_label {
+    background: #117d86;
+    }
+
+#jack_button_label:active {
+    transform: translateY(4px);
+    }
+
+:checked + span {
+    font-weight: bold;
+    box-shadow: none;
+    background: #010102;
+}
+
+.row_selected {
+    background: #ff79ff;
+}
\ No newline at end of file
diff --git a/src/use/tools.py b/src/use/tools.py
old mode 100644
new mode 100755
index 37459bb9..e29a929a
--- a/src/use/tools.py
+++ b/src/use/tools.py
@@ -4,11 +4,12 @@
 
 import ast
 import inspect
+from collections.abc import Callable
+from functools import singledispatch, update_wrapper
 from itertools import takewhile
 from textwrap import dedent
 
-from functools import singledispatch, update_wrapper
-from typing import Callable
+from use import NirvanaWarning
 
 
 class _PipeTransformer(ast.NodeTransformer):
@@ -32,14 +33,17 @@ def visit_BinOp(self, node):
 
 
 # singledispatch for methods
-def methdispatch(func) :
+def methdispatch(func):
     dispatcher = singledispatch(func)
 
-    def wrapper(*args, **kw):
+    def wrapper(*args, **kwargs):
+
         # so we can dispatch on None
         if len(args) == 1:
+            if not kwargs:
+                raise NirvanaWarning("No use trying to use Nothing.")
             args = args + (None,)
-        return dispatcher.dispatch(args[1].__class__)(*args, **kw)
+        return dispatcher.dispatch(args[1].__class__)(*args, **kwargs)
 
     wrapper.register = dispatcher.register
     update_wrapper(wrapper, func)
@@ -47,6 +51,8 @@ def wrapper(*args, **kw):
 
 
 def pipes(func_or_class):
+    from beartype import beartype
+
     if inspect.isclass(func_or_class):
         decorator_frame = inspect.stack()[1]
         ctx = decorator_frame[0].f_locals


Test patch:
diff --git a/pytest.ini b/pytest.ini
index 4e6e1c87..6f29f1f6 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -6,6 +6,6 @@ testpaths =
     tests/tdd_test.py
 
 env =
-    PYTHONDEBUGMODE=1
+    PYTHONDEBUGMODE=0
 
 norecursedirs = tests/*
\ No newline at end of file
diff --git a/src/.test4.py b/src/.test4.py
index fb4a645c..582e8ee9 100644
--- a/src/.test4.py
+++ b/src/.test4.py
@@ -1,5 +1,36 @@
-print("start")
-import use
+from time import perf_counter_ns
+from collections import deque, defaultdict
+from functools import wraps
+from typing import DefaultDict, Deque
 
-mod = use("numpy", version="1.0", modes=use.auto_install)
-print("fin")
+from time import perf_counter_ns as time
+from collections import deque, defaultdict
+
+_timings: dict[int, Deque[int]] = DefaultDict(lambda: deque(maxlen=10))
+
+
+def tinny_profiler(func: callable) -> callable:
+    """
+    Decorator to log/track/debug calls and results.
+
+    Args:
+        func (function): The function to decorate.
+    Returns:
+        function: The decorated callable.
+    """
+
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        before = perf_counter_ns()
+        res = func(*args, **kwargs)
+        after = perf_counter_ns()
+        _timings[func].append(after - before)
+        return res
+
+    return wrapper
+
+@tinny_profiler
+def a():
+    print("adsf")
+    
+a()
\ No newline at end of file
diff --git a/src/.test5.py b/src/.test5.py
index a4957944..a6ed57fd 100644
--- a/src/.test5.py
+++ b/src/.test5.py
@@ -1,7 +1,9 @@
-from hashlib import sha256
-from use.hash_alphabet import hexdigest_as_JACK, JACK_as_num, num_as_hexdigest
+import use
+from beartype import beartype
 
-s = b'\x9a\xe6\xa8'
-h = sha256(s).hexdigest()
-
-h == num_as_hexdigest(JACK_as_num(hexdigest_as_JACK(h)))
\ No newline at end of file
+mod = use("requests")
+use.apply_aspect(mod, beartype)
+use.apply_aspect(mod, use.tinny_profiler)
+for _ in range(1000):
+    mod.get("https://pypi.org/project/beartype/")
+use.show_profiling()
\ No newline at end of file
diff --git a/src/.test6.py b/src/.test6.py
new file mode 100644
index 00000000..5889734d
--- /dev/null
+++ b/src/.test6.py
@@ -0,0 +1,7 @@
+class Test:
+    def __init__(self, x):
+        self.x = x
+
+    def foo(self):
+        return self.x
+
diff --git a/src/.test7.py b/src/.test7.py
new file mode 100644
index 00000000..704cacfb
--- /dev/null
+++ b/src/.test7.py
@@ -0,0 +1,8 @@
+import use
+
+use(
+    "pyspark",
+    version="3.1.2",
+    modes=use.auto_install,
+    hash_algo=use.Hash.sha256,
+)
diff --git a/src/.test8.py b/src/.test8.py
new file mode 100644
index 00000000..51c79eda
--- /dev/null
+++ b/src/.test8.py
@@ -0,0 +1,6 @@
+
+hits = {1,2,3}
+
+
+print(list(hits))
+    
\ No newline at end of file
diff --git a/tests/.tests/.file_for_test387.py b/tests/.tests/.file_for_test387.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/.tests/.test0.py b/tests/.tests/.test0.py
new file mode 100644
index 00000000..c8a78e52
--- /dev/null
+++ b/tests/.tests/.test0.py
@@ -0,0 +1,27 @@
+import inspect, ast, dis
+
+
+def decorator(func):
+    def wrapper(x):
+        return func(x + 1)
+    return wrapper
+
+
+def test(x):
+    frame = inspect.currentframe()
+    while True:
+        if frame.f_code == a.__code__:
+            print(frame.f_code == a.__code__)
+            break
+        else:
+            frame = frame.f_back
+
+    return x**2
+
+
+def a():
+    test(2)
+    
+a()
+
+
diff --git a/tests/.tests/.test2.py b/tests/.tests/.test2.py
index 5043abaf..00d46f57 100644
--- a/tests/.tests/.test2.py
+++ b/tests/.tests/.test2.py
@@ -1,3 +1,204 @@
+from time import perf_counter_ns
+from statistics import mean, stdev, median
+from enum import Enum, IntEnum
+
+import numpy as np
 import sys
 
-print(sys.argsv)
\ No newline at end of file
+from numba import jit, njit
+
+from random import randint, random
+from fuzzylogic.functions import gauss
+
+from random import randint
+from math import exp, sqrt
+
+# random_vars = [randint(0, 100) for _ in range(10000000)]
+
+def linear(m:float=0, b:float=0) -> callable:
+    """A textbook linear function with y-axis section and gradient.
+    
+    f(x) = m*x + b
+    BUT CLIPPED.
+
+    >>> f = linear(1, -1)
+    >>> f(-2)   # should be -3 but clipped
+    0
+    >>> f(0)    # should be -1 but clipped
+    0
+    >>> f(1)
+    0
+    >>> f(1.5)
+    0.5
+    >>> f(2)
+    1
+    >>> f(3)    # should be 2 but clipped
+    1
+    """
+    def f(x) -> float:
+        y = m * x + b
+        if y <= 0:
+            return 0
+        elif y >= 1:
+            return 1
+        else:
+            return y
+    return f
+
+def test_linear():
+    f = linear(0, 100)
+    return [f(x) for x in random_vars]
+
+def linear_numba(m:float=0, b:float=0) -> callable:
+    """A textbook linear function with y-axis section and gradient.
+    
+    f(x) = m*x + b
+    BUT CLIPPED.
+
+    >>> f = linear(1, -1)
+    >>> f(-2)   # should be -3 but clipped
+    0
+    >>> f(0)    # should be -1 but clipped
+    0
+    >>> f(1)
+    0
+    >>> f(1.5)
+    0.5
+    >>> f(2)
+    1
+    >>> f(3)    # should be 2 but clipped
+    1
+    """
+    @njit
+    def f(x:float) -> float:
+        y = m * x + b
+        if y <= 0:
+            return 0
+        elif y >= 1:
+            return 1
+        else:
+            return y
+    return f
+
+def test_linear_numba():
+    f = linear_numba(0, 100)
+    return [f(x) for x in random_vars]
+
+def gauss_numba(c:float, b:float, *, c_m=1):
+    """Defined by ae^(-b(x-x0)^2), a gaussian distribution.
+    
+    Basically a triangular sigmoid function, it comes close to human perception.
+
+    vars
+    ----
+    c_m (a)
+        defines the maximum y-value of the graph
+    b
+        defines the steepness
+    c (x0)
+        defines the symmetry center/peak of the graph
+    """
+    assert 0 < c_m <= 1
+    assert 0 < b, "b must be greater than 0"
+    max_float = sqrt(sys.float_info.max)
+
+    @njit
+    def f(x):
+        # instead of try-except OverflowError
+        if (x-c) > max_float:
+            return 0
+        else:
+            o = (x - c)**2
+        return c_m * exp(-b * o)
+    return f
+
+
+def gauss(c:float, b:float, *, c_m=1):
+    """Defined by ae^(-b(x-x0)^2), a gaussian distribution.
+    
+    Basically a triangular sigmoid function, it comes close to human perception.
+
+    vars
+    ----
+    c_m (a)
+        defines the maximum y-value of the graph
+    b
+        defines the steepness
+    c (x0)
+        defines the symmetry center/peak of the graph
+    """
+    assert 0 < c_m <= 1
+    assert 0 < b, "b must be greater than 0"
+    max_float = sqrt(sys.float_info.max)
+
+    def f(x):
+        # instead of try-except OverflowError
+        if (x-c) > max_float:
+            return 0
+        else:
+            o = (x - c)**2
+        return c_m * exp(-b * o)
+    return f
+
+def gaussmf(x, mean, sigma):
+    """
+    Gaussian fuzzy membership function.
+    Parameters
+    ----------
+    x : 1d array or iterable
+        Independent variable.
+    mean : float
+        Gaussian parameter for center (mean) value.
+    sigma : float
+        Gaussian parameter for standard deviation.
+    Returns
+    -------
+    y : 1d array
+        Gaussian membership function for x.
+    """
+    return np.exp(-((x - mean)**2.) / (2 * sigma**2.))
+
+def test_gauss_numba():
+    f = gauss_numba(0, 10)
+    return [f(x) for x in random_vars]
+
+def test_gauss():
+    f = gauss(0, 10)
+    return [f(x) for x in random_vars]
+
+def test_gaussmf():
+    return [gaussmf(x, 0, 10) for x in random_vars]
+
+from time import sleep
+test_funcs = [
+    lambda: list({}),
+    lambda: list({1: 2}),
+    lambda: list({(1, 2, 3): 4}),
+    lambda: [3, 3, 4],
+    lambda: [],
+    lambda: list({0, 1, 2, 3, ...}),
+    lambda: list({3, 9, 9}),
+    lambda: list(set()),
+    lambda: [],
+    lambda: [1, 2, 1, 1],
+    lambda: sleep(0.5),
+]
+
+def timeit(func):
+    res = []
+    for _ in range(100):
+        before = perf_counter_ns()
+        func()
+        res.append(perf_counter_ns() - before)
+    for f in (min, mean, median, stdev):
+        print(f.__name__, f"{f(res)} ns ({round(f(res)/10**9,5)} s)")
+    return res
+
+
+import inspect
+
+for f in test_funcs:
+    print("####################")
+    print(inspect.getsource(f))
+    timeit(f)
+    
\ No newline at end of file
diff --git a/tests/.tests/.test3.py b/tests/.tests/.test3.py
index 734c8379..92cc3e76 100644
--- a/tests/.tests/.test3.py
+++ b/tests/.tests/.test3.py
@@ -1,15 +1,1 @@
-import inspect
-
-def get_keyword_param_names(locs):
-    func = globals()[inspect.stack()[1].__code__.co_varnames]
-    print(func, type(func))
-    return {name:locs[name] for name, param in inspect.signature(func).parameters.items()
-        if (
-            param.kind is inspect.Parameter.KEYWORD_ONLY
-            or param.kind is inspect.Parameter.VAR_KEYWORD
-        )}
-
-def foo(*, a, b, **kwargs):
-    return get_keyword_param_names(locals())
-s = foo(a=2, b=3)
-print(s)
\ No newline at end of file
+print("FOO!")
\ No newline at end of file
diff --git a/tests/.tests/.test5.py b/tests/.tests/.test5.py
deleted file mode 100644
index 098f6dd4..00000000
--- a/tests/.tests/.test5.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from functools import lru_cache
-
-@lru_cache
-def fib(n):
-    if n in (0,1):
-        return n
-    return fib(n-1) + fib(n -2)
-
-class Recurse(Exception):
-    def __init__(self, *args, **kwargs):
-        self.args = args
-        self.kwargs = kwargs
-
-def recurse(*args, **kwargs):
-    raise Recurse(*args, **kwargs)
-        
-def tail_recursive(f):
-    def decorated(*args, **kwargs):
-        while True:
-            try:
-                return f(*args, **kwargs)
-            except Recurse as r:
-                args = r.args
-                kwargs = r.kwargs
-                continue
-    return decorated
-
-@tail_recursive
-def factorial_tail(n, accumulator=1):
-    if n == 0:
-        return accumulator
-    recurse(n-1, accumulator=accumulator*n)
-    
-@lru_cache
-def factorial_lru(n):
-    if n in (0, 1):
-        return 1
-    return factorial_lru(n-1) * n
-
-#print(factorial_lru(100))
-#print(factorial_tail(100))
-
-from time import perf_counter_ns
-from statistics import mean, stdev, median
-
-results = []
-for x in range(10000):
-    before = perf_counter_ns()
-    factorial_lru(200)
-    foo = perf_counter_ns()-before
-    results.append(foo)
-print([f(results) for f in [min, max, median, mean, stdev]])
-
-results = []
-for _ in range(10000):
-    before = perf_counter_ns()
-    factorial_tail(200)
-    foo = perf_counter_ns()-before
-    results.append(foo)
-print([f(results) for f in [min, max, median, mean, stdev]])
-
diff --git a/tests/.tests/discord_enum.py b/tests/.tests/discord_enum.py
new file mode 100644
index 00000000..d4200a41
--- /dev/null
+++ b/tests/.tests/discord_enum.py
@@ -0,0 +1,390 @@
+# -*- coding: utf-8 -*-
+
+"""
+The MIT License (MIT)
+
+Copyright (c) 2015-2019 Rapptz
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+"""
+
+import types
+import inspect
+from collections import namedtuple
+from operator import attrgetter
+
+__all__ = (
+    'Enum',
+    'ChannelType',
+    'MessageType',
+    'VoiceRegion',
+    'SpeakingState',
+    'VerificationLevel',
+    'ContentFilter',
+    'Status',
+    'DefaultAvatar',
+    'RelationshipType',
+    'AuditLogAction',
+    'AuditLogActionCategory',
+    'UserFlags',
+    'ActivityType',
+    'HypeSquadHouse',
+    'NotificationLevel',
+    'PremiumType',
+    'UserContentFilter',
+    'FriendFlags',
+    'Theme',
+)
+
+def _create_value_cls(name):
+    cls = namedtuple(f'_EnumValue_{name}', 'name value')
+    cls.__repr__ = lambda self: '<%s.%s: %r>' % (name, self.name, self.value)
+    cls.__str__ = lambda self: f'{name}.{self.name}'
+    return cls
+
+class EnumMeta(type):
+    def __new__(cls, name, bases, attrs):
+        value_mapping = {}
+        member_mapping = {}
+        member_names = []
+
+        value_cls = _create_value_cls(name)
+        for key, value in list(attrs.items()):
+            is_function = isinstance(value, types.FunctionType)
+            if key[0] == '_' and not is_function:
+                continue
+
+            if is_function:
+                setattr(value_cls, key, value)
+                del attrs[key]
+                continue
+
+            if inspect.ismethoddescriptor(value):
+                continue
+
+            try:
+                new_value = value_mapping[value]
+            except KeyError:
+                new_value = value_cls(name=key, value=value)
+                value_mapping[value] = new_value
+                member_names.append(key)
+
+            member_mapping[key] = new_value
+            attrs[key] = new_value
+
+        attrs['_enum_value_map_'] = value_mapping
+        attrs['_enum_member_map_'] = member_mapping
+        attrs['_enum_member_names_'] = member_names
+        actual_cls = super().__new__(cls, name, bases, attrs)
+        value_cls._actual_enum_cls_ = actual_cls
+        return actual_cls
+
+    def __iter__(cls):
+        return (cls._enum_member_map_[name] for name in cls._enum_member_names_)
+
+    def __repr__(cls):
+        return '<enum %r>' % cls.__name__
+
+    @property
+    def __members__(cls):
+        return types.MappingProxyType(cls._enum_member_map_)
+
+    def __call__(cls, value):
+        try:
+            return cls._enum_value_map_[value]
+        except (KeyError, TypeError):
+            raise ValueError("%r is not a valid %s" % (value, cls.__name__))
+
+    def __getitem__(cls, key):
+        return cls._enum_member_map_[key]
+
+    def __setattr__(cls, name, value):
+        raise TypeError('Enums are immutable.')
+
+    def __instancecheck__(self, instance):
+        # isinstance(x, Y)
+        # -> __instancecheck__(Y, x)
+        try:
+            return instance._actual_enum_cls_ is self
+        except AttributeError:
+            return False
+
+class Enum(metaclass=EnumMeta):
+    @classmethod
+    def try_value(cls, value):
+        try:
+            return cls._enum_value_map_[value]
+        except (KeyError, TypeError):
+            return value
+
+
+class ChannelType(Enum):
+    text     = 0
+    private  = 1
+    voice    = 2
+    group    = 3
+    category = 4
+    news     = 5
+    store    = 6
+
+    def __str__(self):
+        return self.name
+
+class MessageType(Enum):
+    default                    = 0
+    recipient_add              = 1
+    recipient_remove           = 2
+    call                       = 3
+    channel_name_change        = 4
+    channel_icon_change        = 5
+    pins_add                   = 6
+    new_member                 = 7
+    premium_guild_subscription = 8
+    premium_guild_tier_1       = 9
+    premium_guild_tier_2       = 10
+    premium_guild_tier_3       = 11
+
+class VoiceRegion(Enum):
+    us_west       = 'us-west'
+    us_east       = 'us-east'
+    us_south      = 'us-south'
+    us_central    = 'us-central'
+    eu_west       = 'eu-west'
+    eu_central    = 'eu-central'
+    singapore     = 'singapore'
+    london        = 'london'
+    sydney        = 'sydney'
+    amsterdam     = 'amsterdam'
+    frankfurt     = 'frankfurt'
+    brazil        = 'brazil'
+    hongkong      = 'hongkong'
+    russia        = 'russia'
+    japan         = 'japan'
+    southafrica   = 'southafrica'
+    india         = 'india'
+    vip_us_east   = 'vip-us-east'
+    vip_us_west   = 'vip-us-west'
+    vip_amsterdam = 'vip-amsterdam'
+
+    def __str__(self):
+        return self.value
+
+class SpeakingState(Enum):
+    none       = 0
+    voice      = 1
+    soundshare = 2
+    priority   = 4
+
+    def __str__(self):
+        return self.name
+
+    def __int__(self):
+        return self.value
+
+class VerificationLevel(Enum):
+    none              = 0
+    low               = 1
+    medium            = 2
+    high              = 3
+    table_flip        = 3
+    extreme           = 4
+    double_table_flip = 4
+
+    def __str__(self):
+        return self.name
+
+class ContentFilter(Enum):
+    disabled    = 0
+    no_role     = 1
+    all_members = 2
+
+    def __str__(self):
+        return self.name
+
+class UserContentFilter(Enum):
+    disabled    = 0
+    friends     = 1
+    all_messages = 2
+
+class FriendFlags(Enum):
+    noone = 0
+    mutual_guilds = 1
+    mutual_friends = 2
+    guild_and_friends = 3
+    everyone = 4
+
+class Theme(Enum):
+    light = 'light'
+    dark = 'dark'
+
+class Status(Enum):
+    online = 'online'
+    offline = 'offline'
+    idle = 'idle'
+    dnd = 'dnd'
+    do_not_disturb = 'dnd'
+    invisible = 'invisible'
+
+    def __str__(self):
+        return self.value
+
+class DefaultAvatar(Enum):
+    blurple = 0
+    grey    = 1
+    gray    = 1
+    green   = 2
+    orange  = 3
+    red     = 4
+
+    def __str__(self):
+        return self.name
+
+class RelationshipType(Enum):
+    friend           = 1
+    blocked          = 2
+    incoming_request = 3
+    outgoing_request = 4
+
+class NotificationLevel(Enum):
+    all_messages  = 0
+    only_mentions = 1
+
+class AuditLogActionCategory(Enum):
+    create = 1
+    delete = 2
+    update = 3
+
+class AuditLogAction(Enum):
+    guild_update             = 1
+    channel_create           = 10
+    channel_update           = 11
+    channel_delete           = 12
+    overwrite_create         = 13
+    overwrite_update         = 14
+    overwrite_delete         = 15
+    kick                     = 20
+    member_prune             = 21
+    ban                      = 22
+    unban                    = 23
+    member_update            = 24
+    member_role_update       = 25
+    role_create              = 30
+    role_update              = 31
+    role_delete              = 32
+    invite_create            = 40
+    invite_update            = 41
+    invite_delete            = 42
+    webhook_create           = 50
+    webhook_update           = 51
+    webhook_delete           = 52
+    emoji_create             = 60
+    emoji_update             = 61
+    emoji_delete             = 62
+    message_delete           = 72
+
+    @property
+    def category(self):
+        lookup = {
+            AuditLogAction.guild_update:       AuditLogActionCategory.update,
+            AuditLogAction.channel_create:     AuditLogActionCategory.create,
+            AuditLogAction.channel_update:     AuditLogActionCategory.update,
+            AuditLogAction.channel_delete:     AuditLogActionCategory.delete,
+            AuditLogAction.overwrite_create:   AuditLogActionCategory.create,
+            AuditLogAction.overwrite_update:   AuditLogActionCategory.update,
+            AuditLogAction.overwrite_delete:   AuditLogActionCategory.delete,
+            AuditLogAction.kick:               None,
+            AuditLogAction.member_prune:       None,
+            AuditLogAction.ban:                None,
+            AuditLogAction.unban:              None,
+            AuditLogAction.member_update:      AuditLogActionCategory.update,
+            AuditLogAction.member_role_update: AuditLogActionCategory.update,
+            AuditLogAction.role_create:        AuditLogActionCategory.create,
+            AuditLogAction.role_update:        AuditLogActionCategory.update,
+            AuditLogAction.role_delete:        AuditLogActionCategory.delete,
+            AuditLogAction.invite_create:      AuditLogActionCategory.create,
+            AuditLogAction.invite_update:      AuditLogActionCategory.update,
+            AuditLogAction.invite_delete:      AuditLogActionCategory.delete,
+            AuditLogAction.webhook_create:     AuditLogActionCategory.create,
+            AuditLogAction.webhook_update:     AuditLogActionCategory.update,
+            AuditLogAction.webhook_delete:     AuditLogActionCategory.delete,
+            AuditLogAction.emoji_create:       AuditLogActionCategory.create,
+            AuditLogAction.emoji_update:       AuditLogActionCategory.update,
+            AuditLogAction.emoji_delete:       AuditLogActionCategory.delete,
+            AuditLogAction.message_delete:     AuditLogActionCategory.delete,
+        }
+        return lookup[self]
+
+    @property
+    def target_type(self):
+        v = self.value
+        if v == -1:
+            return 'all'
+        elif v < 10:
+            return 'guild'
+        elif v < 20:
+            return 'channel'
+        elif v < 30:
+            return 'user'
+        elif v < 40:
+            return 'role'
+        elif v < 50:
+            return 'invite'
+        elif v < 60:
+            return 'webhook'
+        elif v < 70:
+            return 'emoji'
+        elif v < 80:
+            return 'message'
+
+class UserFlags(Enum):
+    staff = 1
+    partner = 2
+    hypesquad = 4
+    bug_hunter = 8
+    hypesquad_bravery = 64
+    hypesquad_brilliance = 128
+    hypesquad_balance = 256
+    early_supporter = 512
+
+class ActivityType(Enum):
+    unknown = -1
+    playing = 0
+    streaming = 1
+    listening = 2
+    watching = 3
+
+class HypeSquadHouse(Enum):
+    bravery = 1
+    brilliance = 2
+    balance = 3
+
+class PremiumType(Enum):
+    nitro_classic = 1
+    nitro = 2
+
+def try_enum(cls, val):
+    """A function that tries to turn the value into enum ``cls``.
+
+    If it fails it returns the value instead.
+    """
+
+    try:
+        return cls._enum_value_map_[val]
+    except (KeyError, TypeError, AttributeError):
+        return val
\ No newline at end of file
diff --git a/tests/__init__.py b/tests/__init__.py
index 9be276db..285aa832 100755
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -8,11 +8,16 @@
 if sys.version_info < (3, 9) and "use" not in sys.modules:
     import gc, types, typing
     from typing import _GenericAlias as GenericAlias
-    for t in (list, dict, set, tuple, frozenset):
-      r = gc.get_referents(t.__dict__)[0]
-      r.update({
-        "__class_getitem__": classmethod(GenericAlias),
-      })
+    from abc import ABCMeta
+    from collections.abc import Callable
+    from types import CellType
+    for t in (list, dict, set, tuple, frozenset, ABCMeta, Callable, CellType):
+        r = gc.get_referents(t.__dict__)[0]
+        r.update(
+            {
+                "__class_getitem__": classmethod(GenericAlias),
+            }
+        )
 
 
 import use
diff --git a/tests/beast.py b/tests/beast.py
new file mode 100644
index 00000000..8291bff0
--- /dev/null
+++ b/tests/beast.py
@@ -0,0 +1,122 @@
+import io
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import warnings
+from collections.abc import Callable
+from contextlib import AbstractContextManager, closing, redirect_stdout
+from datetime import datetime
+from hashlib import sha256
+from importlib.metadata import PackageNotFoundError, distribution
+from importlib.util import find_spec
+from pathlib import Path
+from subprocess import STDOUT, check_output
+from textwrap import dedent
+from threading import _shutdown_locks
+from time import time
+from types import ModuleType
+from unittest.mock import patch
+from warnings import catch_warnings, filterwarnings
+
+import packaging.tags
+import packaging.version
+import pytest
+import requests
+from furl import furl as URL
+from hypothesis import assume, example, given
+from hypothesis import strategies as st
+from pytest import fixture, mark, raises, skip
+
+src = import_base = Path(__file__).parent.parent / "src"
+cwd = Path().cwd()
+os.chdir(src)
+sys.path.insert(0, "") if "" not in sys.path else None
+
+if sys.version_info < (3, 9) and "use" not in sys.modules:
+    import gc
+    import types
+    import typing
+    from abc import ABCMeta
+    from collections.abc import Callable
+    from types import CellType
+    from typing import _GenericAlias as GenericAlias
+
+    for t in (list, dict, set, tuple, frozenset, ABCMeta, Callable, CellType):
+        r = gc.get_referents(t.__dict__)[0]
+        r.update(
+            {
+                "__class_getitem__": classmethod(GenericAlias),
+            }
+        )
+
+
+import use
+
+os.chdir(cwd)
+
+is_win = sys.platform.startswith("win")
+
+__package__ = "tests"
+import json
+import logging
+
+from use import auto_install, fatal_exceptions, no_cleanup, use
+from use.hash_alphabet import JACK_as_num, hexdigest_as_JACK, is_JACK, num_as_hexdigest
+from use.pimp import _parse_name
+from use.pydantics import JustUse_Info, PyPI_Project, PyPI_Release, Version
+
+log = logging.getLogger(".".join((__package__, __name__)))
+log.setLevel(logging.DEBUG if "DEBUG" in os.environ else logging.NOTSET)
+
+use.config.testing = True
+
+
+@fixture()
+def reuse():
+    """
+    Return the `use` module in a clean state for "reuse."
+
+    NOTE: making a completely new one each time would take
+    ages, due to expensive _registry setup, re-downloading
+    venv packages, etc., so if we are careful to reset any
+    additional state changes on a case-by-case basis,
+    this approach is more efficient and is the clear winner.
+    """
+    use._using.clear()
+    use.main._reloaders.clear()
+    return use
+
+
+p = Path(__file__).parent / "beast_data.json"
+
+with open(p) as file:
+    data = json.load(file)
+
+begin = time()
+
+
+@mark.parametrize("package_name, module_name, version", data)
+def test_mass(reuse, package_name, module_name, version):
+    """
+    Taken from the original beast test suite, and boiled down to the bare minimum.
+    """
+    with patch("webbrowser.open"), io.StringIO() as buf, redirect_stdout(buf):
+        try:
+            mod = reuse(package_name, version=version, modes=reuse.auto_install)
+            assert False, f"Actually returned mod: {mod}"
+        except RuntimeWarning:
+            recommended_hash = buf.getvalue().splitlines()[-1].strip()
+        mod = reuse(
+            package_name=package_name,
+            module_name="",
+            version=version,
+            hashes=recommended_hash,
+            modes=reuse.auto_install | reuse.no_cleanup,
+        )
+        assert isinstance(mod, ModuleType)
+
+
+print("============================================================")
+print("ran", len(data), "tests in", (time() - begin) // 60, "minutes")
diff --git a/tests/beast_data.json b/tests/beast_data.json
new file mode 100644
index 00000000..57c4f197
--- /dev/null
+++ b/tests/beast_data.json
@@ -0,0 +1,6607 @@
+[
+    [
+        "tensorflow",
+        "tensorflow",
+        "2.6.0"
+    ],
+    [
+        "tensorflow-gpu",
+        "",
+        "2.6.0"
+    ],
+    [
+        "django",
+        "django",
+        "4.0a1"
+    ],
+    [
+        "flask",
+        "flask",
+        "2.0.2"
+    ],
+    [
+        "keras",
+        "keras",
+        "2.7.0rc0"
+    ],
+    [
+        "transformers",
+        "transformers",
+        "4.11.2"
+    ],
+    [
+        "protobuf",
+        "protobuf",
+        "4.0.0rc2"
+    ],
+    [
+        "scikit-learn",
+        "sklearn",
+        "1.0"
+    ],
+    [
+        "requests",
+        "requests",
+        "2.26.0"
+    ],
+    [
+        "scrapy",
+        "scrapy",
+        "2.5.0"
+    ],
+    [
+        "pandas",
+        "pandas",
+        "1.3.3"
+    ],
+    [
+        "pyspark",
+        "pyspark",
+        "3.1.2"
+    ],
+    [
+        "airflow",
+        "airflow",
+        "0.6"
+    ],
+    [
+        "black",
+        "black",
+        "21.9b0"
+    ],
+    [
+        "selenium",
+        "selenium",
+        "4.0.0rc2"
+    ],
+    [
+        "xgboost",
+        "xgboost",
+        "1.5.0rc1"
+    ],
+    [
+        "spacy",
+        "spacy",
+        "3.1.3"
+    ],
+    [
+        "mxnet",
+        "mxnet",
+        "2.0.0a0"
+    ],
+    [
+        "mxnet-mkl",
+        "mxnet-mkl",
+        "1.6.0"
+    ],
+    [
+        "tqdm",
+        "tqdm",
+        "4.62.3"
+    ],
+    [
+        "numpy",
+        "numpy",
+        "1.21.2"
+    ],
+    [
+        "celery",
+        "celery",
+        "5.2.0rc1"
+    ],
+    [
+        "flatbuffers",
+        "flatbuffers",
+        "2.0"
+    ],
+    [
+        "poetry",
+        "poetry",
+        "1.2.0a2"
+    ],
+    [
+        "bokeh",
+        "bokeh",
+        "2.4.0"
+    ],
+    [
+        "cookiecutter",
+        "cookiecutter",
+        "1.7.3"
+    ],
+    [
+        "luigi",
+        "luigi",
+        "3.0.3"
+    ],
+    [
+        "ipython",
+        "ipython",
+        "7.28.0"
+    ],
+    [
+        "matplotlib",
+        "matplotlib",
+        "3.5.0rc1"
+    ],
+    [
+        "faker",
+        "faker",
+        "8.16.0"
+    ],
+    [
+        "lightgbm",
+        "lightgbm",
+        "3.2.1"
+    ],
+    [
+        "imgaug",
+        "imgaug",
+        "0.4.0"
+    ],
+    [
+        "aiohttp",
+        "aiohttp",
+        "4.0.0a1"
+    ],
+    [
+        "click",
+        "click",
+        "8.0.1"
+    ],
+    [
+        "brotli",
+        "brotli",
+        "1.0.9"
+    ],
+    [
+        "plotly",
+        "plotly",
+        "5.3.1"
+    ],
+    [
+        "nltk",
+        "nltk",
+        "3.6.4"
+    ],
+    [
+        "datasets",
+        "datasets",
+        "1.12.1"
+    ],
+    [
+        "torchvision",
+        "torchvision",
+        "0.10.1"
+    ],
+    [
+        "pybind11",
+        "pybind11",
+        "2.8.0"
+    ],
+    [
+        "networkx",
+        "networkx",
+        "2.6.3"
+    ],
+    [
+        "redis",
+        "redis",
+        "3.5.3"
+    ],
+    [
+        "pillow",
+        "pillow",
+        "8.3.2"
+    ],
+    [
+        "dask",
+        "dask",
+        "2021.9.1"
+    ],
+    [
+        "seaborn",
+        "seaborn",
+        "0.11.2"
+    ],
+    [
+        "scipy",
+        "scipy",
+        "1.7.1"
+    ],
+    [
+        "aws-sam-translator",
+        "aws-sam-translator",
+        "1.39.0"
+    ],
+    [
+        "uvloop",
+        "uvloop",
+        "0.16.0"
+    ],
+    [
+        "pandas-profiling",
+        "pandas-profiling",
+        "3.1.0"
+    ],
+    [
+        "jinja2",
+        "jinja2",
+        "3.0.2"
+    ],
+    [
+        "gunicorn",
+        "gunicorn",
+        "20.1.0"
+    ],
+    [
+        "pytest",
+        "pytest",
+        "6.2.5"
+    ],
+    [
+        "httpx",
+        "httpx",
+        "1.0.0b0"
+    ],
+    [
+        "psutil",
+        "psutil",
+        "5.8.0"
+    ],
+    [
+        "pydantic",
+        "pydantic",
+        "1.8.2"
+    ],
+    [
+        "arrow",
+        "arrow",
+        "1.2.0"
+    ],
+    [
+        "pip",
+        "pip",
+        "21.2.4"
+    ],
+    [
+        "prompt_toolkit",
+        "prompt_toolkit",
+        "3.0.20"
+    ],
+    [
+        "prompt-toolkit",
+        "prompt-toolkit",
+        "3.0.20"
+    ],
+    [
+        "ninja",
+        "ninja",
+        "1.10.2.2"
+    ],
+    [
+        "paramiko",
+        "paramiko",
+        "2.7.2"
+    ],
+    [
+        "tensorboardx",
+        "tensorboardx",
+        "2.4"
+    ],
+    [
+        "altair",
+        "altair",
+        "4.1.0"
+    ],
+    [
+        "scapy",
+        "scapy",
+        "2.4.5"
+    ],
+    [
+        "boto3",
+        "boto3",
+        "1.18.54"
+    ],
+    [
+        "statsmodels",
+        "statsmodels",
+        "0.13.0"
+    ],
+    [
+        "pymysql",
+        "pymysql",
+        "1.0.2"
+    ],
+    [
+        "boto",
+        "boto",
+        "2.49.0"
+    ],
+    [
+        "modin",
+        "modin",
+        "0.11.0"
+    ],
+    [
+        "jupyterhub",
+        "jupyterhub",
+        "2.0.0b2"
+    ],
+    [
+        "typer",
+        "typer",
+        "0.4.0"
+    ],
+    [
+        "pymc3",
+        "pymc3",
+        "3.11.4"
+    ],
+    [
+        "pymc",
+        "pymc",
+        "2.3.8"
+    ],
+    [
+        "sh",
+        "sh",
+        "1.14.2"
+    ],
+    [
+        "werkzeug",
+        "werkzeug",
+        "2.0.1"
+    ],
+    [
+        "marshmallow",
+        "marshmallow",
+        "3.13.0"
+    ],
+    [
+        "tensorboard-data-server",
+        "tensorboard-data-server",
+        "0.6.1"
+    ],
+    [
+        "tensorboard",
+        "tensorboard",
+        "2.6.0"
+    ],
+    [
+        "tensorflow-tensorboard",
+        "tensorflow-tensorboard",
+        "1.5.1"
+    ],
+    [
+        "gevent",
+        "gevent",
+        "21.8.0"
+    ],
+    [
+        "docker-py",
+        "docker-py",
+        "1.10.6"
+    ],
+    [
+        "hypothesis",
+        "hypothesis",
+        "6.23.1"
+    ],
+    [
+        "cupy",
+        "cupy",
+        "10.0.0b3"
+    ],
+    [
+        "moto",
+        "moto",
+        "2.2.9.dev16"
+    ],
+    [
+        "asyncpg",
+        "asyncpg",
+        "0.24.0"
+    ],
+    [
+        "jedi",
+        "jedi",
+        "0.18.0"
+    ],
+    [
+        "watchdog",
+        "watchdog",
+        "2.1.6"
+    ],
+    [
+        "flower",
+        "flower",
+        "1.0.0"
+    ],
+    [
+        "flask-admin",
+        "flask-admin",
+        "1.5.8"
+    ],
+    [
+        "csvkit",
+        "csvkit",
+        "1.0.6"
+    ],
+    [
+        "pygithub",
+        "pygithub",
+        "1.55"
+    ],
+    [
+        "pendulum",
+        "pendulum",
+        "2.1.2"
+    ],
+    [
+        "xmltodict",
+        "xmltodict",
+        "0.12.0"
+    ],
+    [
+        "scikit-image",
+        "scikit-image",
+        "0.18.3"
+    ],
+    [
+        "flask-socketio",
+        "flask-socketio",
+        "5.1.1"
+    ],
+    [
+        "twisted",
+        "twisted",
+        "21.7.0"
+    ],
+    [
+        "cryptography",
+        "cryptography",
+        "35.0.0"
+    ],
+    [
+        "cryptography-vectors",
+        "cryptography-vectors",
+        "35.0.0"
+    ],
+    [
+        "conda",
+        "conda",
+        "4.3.16"
+    ],
+    [
+        "sqlalchemy",
+        "sqlalchemy",
+        "1.4.25"
+    ],
+    [
+        "trio",
+        "trio",
+        "0.19.0"
+    ],
+    [
+        "isort",
+        "isort",
+        "5.9.3"
+    ],
+    [
+        "sphinx",
+        "sphinx",
+        "4.2.0"
+    ],
+    [
+        "pyjwt",
+        "pyjwt",
+        "2.1.0"
+    ],
+    [
+        "virtualenv",
+        "virtualenv",
+        "20.8.1"
+    ],
+    [
+        "autopep8",
+        "autopep8",
+        "1.5.7"
+    ],
+    [
+        "sphinx_rtd_theme",
+        "sphinx_rtd_theme",
+        "1.0.0"
+    ],
+    [
+        "rapidjson",
+        "rapidjson",
+        "1.0.0"
+    ],
+    [
+        "connexion",
+        "connexion",
+        "2.9.0"
+    ],
+    [
+        "apscheduler",
+        "apscheduler",
+        "3.8.0"
+    ],
+    [
+        "curio",
+        "curio",
+        "1.5"
+    ],
+    [
+        "attrs",
+        "attrs",
+        "21.2.0"
+    ],
+    [
+        "toolz",
+        "toolz",
+        "0.11.1"
+    ],
+    [
+        "flask-sqlalchemy",
+        "flask-sqlalchemy",
+        "2.5.1"
+    ],
+    [
+        "pyramid",
+        "pyramid",
+        "2.0"
+    ],
+    [
+        "pylint",
+        "pylint",
+        "3.0.0a4"
+    ],
+    [
+        "flask-appbuilder",
+        "flask-appbuilder",
+        "3.3.3"
+    ],
+    [
+        "tensorflow-probability",
+        "tensorflow-probability",
+        "0.14.1"
+    ],
+    [
+        "ujson",
+        "ujson",
+        "4.2.0"
+    ],
+    [
+        "pymongo",
+        "pymongo",
+        "3.12.0"
+    ],
+    [
+        "elasticsearch",
+        "elasticsearch",
+        "7.15.0"
+    ],
+    [
+        "jsonschema",
+        "jsonschema",
+        "4.0.1"
+    ],
+    [
+        "elasticsearch-dsl",
+        "elasticsearch-dsl",
+        "7.4.0"
+    ],
+    [
+        "responses",
+        "responses",
+        "0.14.0"
+    ],
+    [
+        "pywin32",
+        "pywin32",
+        "301"
+    ],
+    [
+        "tenacity",
+        "tenacity",
+        "8.0.1"
+    ],
+    [
+        "slackclient",
+        "slackclient",
+        "2.9.3"
+    ],
+    [
+        "slack-sdk",
+        "slack-sdk",
+        "3.11.2"
+    ],
+    [
+        "gitpython",
+        "gitpython",
+        "3.1.24"
+    ],
+    [
+        "rise",
+        "rise",
+        "5.7.1"
+    ],
+    [
+        "memory_profiler",
+        "memory_profiler",
+        "0.58.0"
+    ],
+    [
+        "ftfy",
+        "ftfy",
+        "6.0.3"
+    ],
+    [
+        "freezegun",
+        "freezegun",
+        "1.1.0"
+    ],
+    [
+        "tensorflow-datasets",
+        "tensorflow-datasets",
+        "4.4.0"
+    ],
+    [
+        "tensorflow-hub",
+        "tensorflow-hub",
+        "0.12.0"
+    ],
+    [
+        "missingno",
+        "missingno",
+        "0.5.0"
+    ],
+    [
+        "flask-login",
+        "flask-login",
+        "0.5.0"
+    ],
+    [
+        "fonttools",
+        "fonttools",
+        "4.27.1"
+    ],
+    [
+        "biopython",
+        "biopython",
+        "1.79"
+    ],
+    [
+        "phonenumbers",
+        "phonenumbers",
+        "8.12.33"
+    ],
+    [
+        "urllib3",
+        "urllib3",
+        "1.26.7"
+    ],
+    [
+        "qgrid",
+        "qgrid",
+        "1.3.1"
+    ],
+    [
+        "authlib",
+        "authlib",
+        "1.0.0b1"
+    ],
+    [
+        "xlsxwriter",
+        "xlsxwriter",
+        "3.0.1"
+    ],
+    [
+        "markdown",
+        "markdown",
+        "3.3.4"
+    ],
+    [
+        "factory_boy",
+        "factory_boy",
+        "3.2.0"
+    ],
+    [
+        "colorama",
+        "colorama",
+        "0.4.4"
+    ],
+    [
+        "gpustat",
+        "gpustat",
+        "1.0.0b1"
+    ],
+    [
+        "sqlparse",
+        "sqlparse",
+        "0.4.2"
+    ],
+    [
+        "pyomniscidb",
+        "pyomniscidb",
+        "5.6.4.1"
+    ],
+    [
+        "python-socketio",
+        "python-socketio",
+        "5.4.0"
+    ],
+    [
+        "textdistance",
+        "textdistance",
+        "4.2.1"
+    ],
+    [
+        "shapely",
+        "shapely",
+        "1.8rc1"
+    ],
+    [
+        "pyserial",
+        "pyserial",
+        "3.5"
+    ],
+    [
+        "python-language-server",
+        "python-language-server",
+        "0.36.2"
+    ],
+    [
+        "prometheus_client",
+        "prometheus_client",
+        "0.11.0"
+    ],
+    [
+        "coremltools",
+        "coremltools",
+        "5.0"
+    ],
+    [
+        "thinc",
+        "thinc",
+        "8.0.10"
+    ],
+    [
+        "pycparser",
+        "pycparser",
+        "2.20"
+    ],
+    [
+        "word2vec",
+        "word2vec",
+        "0.11.1"
+    ],
+    [
+        "psycopg2",
+        "psycopg2",
+        "2.9.1"
+    ],
+    [
+        "jupyterlab_widgets",
+        "jupyterlab_widgets",
+        "2.0.0a3"
+    ],
+    [
+        "itsdangerous",
+        "itsdangerous",
+        "2.0.1"
+    ],
+    [
+        "oauthlib",
+        "oauthlib",
+        "3.1.1"
+    ],
+    [
+        "xarray",
+        "xarray",
+        "0.19.0"
+    ],
+    [
+        "smart_open",
+        "smart_open",
+        "5.2.1"
+    ],
+    [
+        "bleach",
+        "bleach",
+        "4.1.0"
+    ],
+    [
+        "markdown2",
+        "markdown2",
+        "2.4.1"
+    ],
+    [
+        "pyodbc",
+        "pyodbc",
+        "4.0.32"
+    ],
+    [
+        "azure-core",
+        "azure-core",
+        "1.19.0"
+    ],
+    [
+        "azure-cosmos",
+        "azure-cosmos",
+        "4.2.0"
+    ],
+    [
+        "azure",
+        "azure",
+        "5.0.0"
+    ],
+    [
+        "pyinotify",
+        "pyinotify",
+        "0.9.6"
+    ],
+    [
+        "more-itertools",
+        "more-itertools",
+        "8.10.0"
+    ],
+    [
+        "pandas-datareader",
+        "pandas-datareader",
+        "0.10.0"
+    ],
+    [
+        "imagehash",
+        "imagehash",
+        "4.2.1"
+    ],
+    [
+        "furl",
+        "furl",
+        "2.1.3"
+    ],
+    [
+        "vcrpy",
+        "vcrpy",
+        "4.1.1"
+    ],
+    [
+        "mpld3",
+        "mpld3",
+        "0.5.5"
+    ],
+    [
+        "pyquery",
+        "pyquery",
+        "1.4.3"
+    ],
+    [
+        "mistune",
+        "mistune",
+        "2.0.0rc1"
+    ],
+    [
+        "mysqlclient",
+        "mysqlclient",
+        "2.0.3"
+    ],
+    [
+        "aioredis",
+        "aioredis",
+        "2.0.0"
+    ],
+    [
+        "httpretty",
+        "httpretty",
+        "1.1.4"
+    ],
+    [
+        "qdarkstyle",
+        "qdarkstyle",
+        "3.0.2"
+    ],
+    [
+        "efficientnet",
+        "efficientnet",
+        "1.1.1"
+    ],
+    [
+        "dateparser",
+        "dateparser",
+        "1.1.0"
+    ],
+    [
+        "keras-applications",
+        "keras-applications",
+        "1.0.8"
+    ],
+    [
+        "requests-futures",
+        "requests-futures",
+        "1.0.0"
+    ],
+    [
+        "twython",
+        "twython",
+        "3.9.1"
+    ],
+    [
+        "absl-py",
+        "absl-py",
+        "0.14.1"
+    ],
+    [
+        "coverage",
+        "coverage",
+        "6.0"
+    ],
+    [
+        "retrying",
+        "retrying",
+        "1.3.3"
+    ],
+    [
+        "pycryptodomex",
+        "pycryptodomex",
+        "3.10.4"
+    ],
+    [
+        "pycryptodome",
+        "pycryptodome",
+        "3.10.4"
+    ],
+    [
+        "cfn-lint",
+        "cfn-lint",
+        "0.54.2"
+    ],
+    [
+        "python-dateutil",
+        "python-dateutil",
+        "2.8.2"
+    ],
+    [
+        "numexpr",
+        "numexpr",
+        "2.7.3"
+    ],
+    [
+        "yq",
+        "yq",
+        "2.12.2"
+    ],
+    [
+        "pythran",
+        "pythran",
+        "0.10.0"
+    ],
+    [
+        "chardet",
+        "chardet",
+        "4.0.0"
+    ],
+    [
+        "pyyaml",
+        "pyyaml",
+        "6.0b1"
+    ],
+    [
+        "ibis-framework",
+        "ibis-framework",
+        "1.4.0"
+    ],
+    [
+        "pyrsistent",
+        "pyrsistent",
+        "0.18.0"
+    ],
+    [
+        "rasterio",
+        "rasterio",
+        "1.2.9"
+    ],
+    [
+        "snakeviz",
+        "snakeviz",
+        "2.1.0"
+    ],
+    [
+        "wrapt",
+        "wrapt",
+        "1.13.0"
+    ],
+    [
+        "flit-core",
+        "flit-core",
+        "3.3.0"
+    ],
+    [
+        "flit",
+        "flit",
+        "3.3.0"
+    ],
+    [
+        "dill",
+        "dill",
+        "0.3.4"
+    ],
+    [
+        "xar",
+        "xar",
+        "20.12.2"
+    ],
+    [
+        "pyhive",
+        "pyhive",
+        "0.6.4"
+    ],
+    [
+        "cherrypy",
+        "cherrypy",
+        "18.6.1"
+    ],
+    [
+        "humanize",
+        "humanize",
+        "3.11.0"
+    ],
+    [
+        "setuptools",
+        "setuptools",
+        "58.2.0"
+    ],
+    [
+        "jmespath",
+        "jmespath",
+        "0.10.0"
+    ],
+    [
+        "jira",
+        "jira",
+        "3.0.1"
+    ],
+    [
+        "simplejson",
+        "simplejson",
+        "3.17.5"
+    ],
+    [
+        "requests-oauthlib",
+        "requests-oauthlib",
+        "1.3.0"
+    ],
+    [
+        "quantecon",
+        "quantecon",
+        "0.5.1"
+    ],
+    [
+        "flask-restx",
+        "flask-restx",
+        "0.5.1"
+    ],
+    [
+        "feedparser",
+        "feedparser",
+        "6.0.8"
+    ],
+    [
+        "greenlet",
+        "greenlet",
+        "1.1.2"
+    ],
+    [
+        "pefile",
+        "pefile",
+        "2021.9.3"
+    ],
+    [
+        "pyparsing",
+        "pyparsing",
+        "3.0.0rc2"
+    ],
+    [
+        "wtforms",
+        "wtforms",
+        "3.0.0a1"
+    ],
+    [
+        "cassandra-driver",
+        "cassandra-driver",
+        "3.25.0"
+    ],
+    [
+        "blessings",
+        "blessings",
+        "1.7"
+    ],
+    [
+        "scons",
+        "scons",
+        "4.2.0"
+    ],
+    [
+        "llvmlite",
+        "llvmlite",
+        "0.37.0"
+    ],
+    [
+        "pywavelets",
+        "pywavelets",
+        "1.1.1"
+    ],
+    [
+        "flask-jwt-extended",
+        "flask-jwt-extended",
+        "4.3.0"
+    ],
+    [
+        "sendgrid",
+        "sendgrid",
+        "6.8.2"
+    ],
+    [
+        "flask-wtf",
+        "flask-wtf",
+        "0.15.1"
+    ],
+    [
+        "rope",
+        "rope",
+        "0.20.1"
+    ],
+    [
+        "aiopg",
+        "aiopg",
+        "1.3.2b1"
+    ],
+    [
+        "nbconvert",
+        "nbconvert",
+        "6.2.0"
+    ],
+    [
+        "simple-salesforce",
+        "simple-salesforce",
+        "1.11.4"
+    ],
+    [
+        "pytest-mock",
+        "pytest-mock",
+        "3.6.1"
+    ],
+    [
+        "twine",
+        "twine",
+        "3.4.2"
+    ],
+    [
+        "quandl",
+        "quandl",
+        "3.6.1"
+    ],
+    [
+        "tabpy-server",
+        "tabpy-server",
+        "0.2"
+    ],
+    [
+        "tabpy-client",
+        "tabpy-client",
+        "0.2"
+    ],
+    [
+        "enaml",
+        "enaml",
+        "0.13.0"
+    ],
+    [
+        "pytest-cov",
+        "pytest-cov",
+        "3.0.0"
+    ],
+    [
+        "python-slugify",
+        "python-slugify",
+        "5.0.2"
+    ],
+    [
+        "doit",
+        "doit",
+        "0.33.1"
+    ],
+    [
+        "arviz",
+        "arviz",
+        "0.11.4"
+    ],
+    [
+        "ws4py",
+        "ws4py",
+        "0.5.1"
+    ],
+    [
+        "stripe",
+        "stripe",
+        "2.60.0"
+    ],
+    [
+        "cachetools",
+        "cachetools",
+        "4.2.4"
+    ],
+    [
+        "progress",
+        "progress",
+        "1.6"
+    ],
+    [
+        "quilt3",
+        "quilt3",
+        "3.5.0"
+    ],
+    [
+        "python-json-logger",
+        "python-json-logger",
+        "2.0.2"
+    ],
+    [
+        "pyflakes",
+        "pyflakes",
+        "2.3.1"
+    ],
+    [
+        "graphviz",
+        "graphviz",
+        "0.17"
+    ],
+    [
+        "pysocks",
+        "pysocks",
+        "1.7.1"
+    ],
+    [
+        "sparkmagic",
+        "sparkmagic",
+        "0.19.1"
+    ],
+    [
+        "hdijupyterutils",
+        "hdijupyterutils",
+        "0.19.1"
+    ],
+    [
+        "autovizwidget",
+        "autovizwidget",
+        "0.19.1"
+    ],
+    [
+        "pandasql",
+        "pandasql",
+        "0.7.3"
+    ],
+    [
+        "botocore",
+        "botocore",
+        "1.21.54"
+    ],
+    [
+        "cloudpickle",
+        "cloudpickle",
+        "2.0.0"
+    ],
+    [
+        "python-levenshtein",
+        "python-levenshtein",
+        "0.12.2"
+    ],
+    [
+        "typing_extensions",
+        "typing_extensions",
+        "3.10.0.2"
+    ],
+    [
+        "typing-extensions",
+        "typing-extensions",
+        "3.10.0.2"
+    ],
+    [
+        "typing",
+        "typing",
+        "3.10.0.0"
+    ],
+    [
+        "argcomplete",
+        "argcomplete",
+        "1.12.3"
+    ],
+    [
+        "keras-preprocessing",
+        "keras-preprocessing",
+        "1.1.2"
+    ],
+    [
+        "streamz",
+        "streamz",
+        "0.6.3"
+    ],
+    [
+        "waitress",
+        "waitress",
+        "2.0.0"
+    ],
+    [
+        "tabulate",
+        "tabulate",
+        "0.8.9"
+    ],
+    [
+        "path.py",
+        "path.py",
+        "12.5.0"
+    ],
+    [
+        "path",
+        "path",
+        "16.2.0"
+    ],
+    [
+        "jsonpickle",
+        "jsonpickle",
+        "2.0.0"
+    ],
+    [
+        "odo",
+        "odo",
+        "0.5.0"
+    ],
+    [
+        "python-jose",
+        "python-jose",
+        "3.3.0"
+    ],
+    [
+        "html5lib",
+        "html5lib",
+        "1.1"
+    ],
+    [
+        "bcolz",
+        "bcolz",
+        "1.2.1"
+    ],
+    [
+        "imageio",
+        "imageio",
+        "2.9.0"
+    ],
+    [
+        "cartopy",
+        "cartopy",
+        "0.20.0"
+    ],
+    [
+        "petl",
+        "petl",
+        "1.7.4"
+    ],
+    [
+        "keras-ocr",
+        "keras-ocr",
+        "0.8.7"
+    ],
+    [
+        "asgiref",
+        "asgiref",
+        "3.4.1"
+    ],
+    [
+        "line_profiler",
+        "line_profiler",
+        "3.3.1"
+    ],
+    [
+        "apispec",
+        "apispec",
+        "5.1.1"
+    ],
+    [
+        "opentsne",
+        "opentsne",
+        "0.6.0"
+    ],
+    [
+        "pywinrm",
+        "pywinrm",
+        "0.4.2"
+    ],
+    [
+        "sshtunnel",
+        "sshtunnel",
+        "0.4.0"
+    ],
+    [
+        "immutables",
+        "immutables",
+        "0.16"
+    ],
+    [
+        "pyshp",
+        "pyshp",
+        "2.1.3"
+    ],
+    [
+        "sockjs-tornado",
+        "sockjs-tornado",
+        "1.0.7"
+    ],
+    [
+        "fiona",
+        "fiona",
+        "1.8.20"
+    ],
+    [
+        "snakebite",
+        "snakebite",
+        "2.11.0"
+    ],
+    [
+        "sqlalchemy-utils",
+        "sqlalchemy-utils",
+        "0.37.8"
+    ],
+    [
+        "six",
+        "six",
+        "1.16.0"
+    ],
+    [
+        "flask-apscheduler",
+        "flask-apscheduler",
+        "1.12.2"
+    ],
+    [
+        "pydocstyle",
+        "pydocstyle",
+        "6.1.1"
+    ],
+    [
+        "pympler",
+        "pympler",
+        "0.9"
+    ],
+    [
+        "pynacl",
+        "pynacl",
+        "1.4.0"
+    ],
+    [
+        "toml",
+        "toml",
+        "0.10.2"
+    ],
+    [
+        "pytest-benchmark",
+        "pytest-benchmark",
+        "3.4.1"
+    ],
+    [
+        "cytoolz",
+        "cytoolz",
+        "0.11.0"
+    ],
+    [
+        "patsy",
+        "patsy",
+        "0.5.2"
+    ],
+    [
+        "appdirs",
+        "appdirs",
+        "1.4.4"
+    ],
+    [
+        "pytest-asyncio",
+        "pytest-asyncio",
+        "0.15.1"
+    ],
+    [
+        "bcrypt",
+        "bcrypt",
+        "3.2.0"
+    ],
+    [
+        "zarr",
+        "zarr",
+        "2.10.1"
+    ],
+    [
+        "h2",
+        "h2",
+        "4.0.0"
+    ],
+    [
+        "pytest-xdist",
+        "pytest-xdist",
+        "2.4.0"
+    ],
+    [
+        "cx_oracle",
+        "cx_oracle",
+        "8.2.1"
+    ],
+    [
+        "cleo",
+        "cleo",
+        "1.0.0a4"
+    ],
+    [
+        "dask-ml",
+        "dask-ml",
+        "1.9.0"
+    ],
+    [
+        "jpype1",
+        "jpype1",
+        "1.3.0"
+    ],
+    [
+        "opentracing",
+        "opentracing",
+        "2.4.0"
+    ],
+    [
+        "flask_cors",
+        "flask_cors",
+        "3.0.10"
+    ],
+    [
+        "flask-cors",
+        "flask-cors",
+        "3.0.10"
+    ],
+    [
+        "keyring",
+        "keyring",
+        "23.2.1"
+    ],
+    [
+        "libcst",
+        "libcst",
+        "0.3.21"
+    ],
+    [
+        "progressbar2",
+        "progressbar2",
+        "3.53.3"
+    ],
+    [
+        "pdoc3",
+        "pdoc3",
+        "0.10.0"
+    ],
+    [
+        "icalendar",
+        "icalendar",
+        "4.0.7"
+    ],
+    [
+        "fabric3",
+        "fabric3",
+        "1.14.post1"
+    ],
+    [
+        "colorlog",
+        "colorlog",
+        "6.4.1"
+    ],
+    [
+        "aiobotocore",
+        "aiobotocore",
+        "1.4.1"
+    ],
+    [
+        "ecdsa",
+        "ecdsa",
+        "0.18.0b1"
+    ],
+    [
+        "parsel",
+        "parsel",
+        "1.6.0"
+    ],
+    [
+        "python-crfsuite",
+        "python-crfsuite",
+        "0.9.7"
+    ],
+    [
+        "ldap3",
+        "ldap3",
+        "2.9.1"
+    ],
+    [
+        "yarl",
+        "yarl",
+        "1.6.3"
+    ],
+    [
+        "dm-tree",
+        "dm-tree",
+        "0.1.6"
+    ],
+    [
+        "clickhouse-driver",
+        "clickhouse-driver",
+        "0.2.2"
+    ],
+    [
+        "celery-redbeat",
+        "celery-redbeat",
+        "2.0.0"
+    ],
+    [
+        "pluggy",
+        "pluggy",
+        "1.0.0"
+    ],
+    [
+        "dictdiffer",
+        "dictdiffer",
+        "0.9.0"
+    ],
+    [
+        "pyproj",
+        "pyproj",
+        "3.2.1"
+    ],
+    [
+        "flask-caching",
+        "flask-caching",
+        "1.10.1"
+    ],
+    [
+        "python-louvain",
+        "python-louvain",
+        "0.15"
+    ],
+    [
+        "bottleneck",
+        "bottleneck",
+        "1.3.2"
+    ],
+    [
+        "impyla",
+        "impyla",
+        "0.18a1"
+    ],
+    [
+        "python-nvd3",
+        "python-nvd3",
+        "0.15.0"
+    ],
+    [
+        "mysql-python",
+        "mysql-python",
+        "1.2.5"
+    ],
+    [
+        "intake",
+        "intake",
+        "0.6.3"
+    ],
+    [
+        "parsedatetime",
+        "parsedatetime",
+        "2.6"
+    ],
+    [
+        "cached-property",
+        "cached-property",
+        "1.5.2"
+    ],
+    [
+        "decorator",
+        "decorator",
+        "5.1.0"
+    ],
+    [
+        "pymssql",
+        "pymssql",
+        "2.2.2"
+    ],
+    [
+        "nose-parameterized",
+        "nose-parameterized",
+        "0.6.0"
+    ],
+    [
+        "parameterized",
+        "parameterized",
+        "0.8.1"
+    ],
+    [
+        "astor",
+        "astor",
+        "0.8.1"
+    ],
+    [
+        "aredis",
+        "aredis",
+        "1.1.8"
+    ],
+    [
+        "unicodecsv",
+        "unicodecsv",
+        "0.14.1"
+    ],
+    [
+        "pydot",
+        "pydot",
+        "1.4.2"
+    ],
+    [
+        "opencensus-context",
+        "opencensus-context",
+        "0.2.dev0"
+    ],
+    [
+        "opencensus",
+        "opencensus",
+        "0.8.0"
+    ],
+    [
+        "multipledispatch",
+        "multipledispatch",
+        "0.6.0"
+    ],
+    [
+        "treelib",
+        "treelib",
+        "1.6.1"
+    ],
+    [
+        "pyhamcrest",
+        "pyhamcrest",
+        "2.0.2"
+    ],
+    [
+        "inflect",
+        "inflect",
+        "5.3.0"
+    ],
+    [
+        "mpmath",
+        "mpmath",
+        "1.2.1"
+    ],
+    [
+        "natsort",
+        "natsort",
+        "7.1.1"
+    ],
+    [
+        "pycryptosat",
+        "pycryptosat",
+        "0.7.0.dev3"
+    ],
+    [
+        "netcdf4",
+        "netcdf4",
+        "1.5.7"
+    ],
+    [
+        "configargparse",
+        "configargparse",
+        "1.5.3"
+    ],
+    [
+        "qtpy",
+        "qtpy",
+        "1.11.2"
+    ],
+    [
+        "validators",
+        "validators",
+        "0.18.2"
+    ],
+    [
+        "snowballstemmer",
+        "snowballstemmer",
+        "2.1.0"
+    ],
+    [
+        "fastparquet",
+        "fastparquet",
+        "0.7.1"
+    ],
+    [
+        "anyio",
+        "anyio",
+        "3.3.2"
+    ],
+    [
+        "munch",
+        "munch",
+        "2.5.1.dev12"
+    ],
+    [
+        "dataclasses",
+        "dataclasses",
+        "0.8"
+    ],
+    [
+        "kiwisolver",
+        "kiwisolver",
+        "1.3.2"
+    ],
+    [
+        "setuptools_scm",
+        "setuptools_scm",
+        "6.3.2"
+    ],
+    [
+        "setuptools-scm",
+        "setuptools-scm",
+        "6.3.2"
+    ],
+    [
+        "s3fs",
+        "s3fs",
+        "2021.10.0"
+    ],
+    [
+        "pynndescent",
+        "pynndescent",
+        "0.5.4"
+    ],
+    [
+        "statsd",
+        "statsd",
+        "3.3.0"
+    ],
+    [
+        "scandir",
+        "scandir",
+        "1.10.0"
+    ],
+    [
+        "pyte",
+        "pyte",
+        "0.8.0"
+    ],
+    [
+        "bitarray",
+        "bitarray",
+        "2.3.4"
+    ],
+    [
+        "mpc",
+        "mpc",
+        "0.0.4"
+    ],
+    [
+        "certifi",
+        "certifi",
+        "2021.5.30"
+    ],
+    [
+        "opt_einsum",
+        "opt_einsum",
+        "3.3.0"
+    ],
+    [
+        "colorcet",
+        "colorcet",
+        "2.0.6"
+    ],
+    [
+        "geventhttpclient",
+        "geventhttpclient",
+        "1.5.2"
+    ],
+    [
+        "datrie",
+        "datrie",
+        "0.8.2"
+    ],
+    [
+        "email-validator",
+        "email-validator",
+        "1.1.3"
+    ],
+    [
+        "datadog",
+        "datadog",
+        "0.42.0"
+    ],
+    [
+        "colorful",
+        "colorful",
+        "0.6.0a1"
+    ],
+    [
+        "coveralls",
+        "coveralls",
+        "3.2.0"
+    ],
+    [
+        "pypandoc",
+        "pypandoc",
+        "1.6.4"
+    ],
+    [
+        "fastavro",
+        "fastavro",
+        "1.4.5"
+    ],
+    [
+        "mpi4py",
+        "mpi4py",
+        "3.1.1"
+    ],
+    [
+        "lmdb",
+        "lmdb",
+        "1.2.1"
+    ],
+    [
+        "dlpack",
+        "dlpack",
+        "0.1"
+    ],
+    [
+        "pyperformance",
+        "pyperformance",
+        "1.0.2"
+    ],
+    [
+        "automat",
+        "automat",
+        "20.2.0"
+    ],
+    [
+        "jsondiff",
+        "jsondiff",
+        "1.3.0"
+    ],
+    [
+        "google-auth",
+        "google-auth",
+        "2.2.1"
+    ],
+    [
+        "marshmallow-sqlalchemy",
+        "marshmallow-sqlalchemy",
+        "0.26.1"
+    ],
+    [
+        "parso",
+        "parso",
+        "0.8.2"
+    ],
+    [
+        "python3-saml",
+        "python3-saml",
+        "1.12.0"
+    ],
+    [
+        "pivottablejs",
+        "pivottablejs",
+        "0.9.0"
+    ],
+    [
+        "traitlets",
+        "traitlets",
+        "5.1.0"
+    ],
+    [
+        "pytest-html",
+        "pytest-html",
+        "3.1.1"
+    ],
+    [
+        "flask-swagger",
+        "flask-swagger",
+        "0.2.14"
+    ],
+    [
+        "runipy",
+        "runipy",
+        "0.1.5"
+    ],
+    [
+        "intervaltree",
+        "intervaltree",
+        "3.1.0"
+    ],
+    [
+        "pydruid",
+        "pydruid",
+        "0.6.2"
+    ],
+    [
+        "pysnmp",
+        "pysnmp",
+        "4.4.12"
+    ],
+    [
+        "mccabe",
+        "mccabe",
+        "0.6.1"
+    ],
+    [
+        "mpl-scatter-density",
+        "mpl-scatter-density",
+        "0.7"
+    ],
+    [
+        "python-rapidjson",
+        "python-rapidjson",
+        "1.4"
+    ],
+    [
+        "markupsafe",
+        "markupsafe",
+        "2.0.1"
+    ],
+    [
+        "rtree",
+        "rtree",
+        "0.9.7"
+    ],
+    [
+        "python-memcached",
+        "python-memcached",
+        "1.59"
+    ],
+    [
+        "python-openid",
+        "python-openid",
+        "2.2.5"
+    ],
+    [
+        "colander",
+        "colander",
+        "1.8.3"
+    ],
+    [
+        "qtawesome",
+        "qtawesome",
+        "1.0.3"
+    ],
+    [
+        "python-snappy",
+        "python-snappy",
+        "0.6.0"
+    ],
+    [
+        "aiodns",
+        "aiodns",
+        "3.0.0"
+    ],
+    [
+        "cymem",
+        "cymem",
+        "2.0.5"
+    ],
+    [
+        "scp",
+        "scp",
+        "0.14.1"
+    ],
+    [
+        "pyperf",
+        "pyperf",
+        "2.3.0"
+    ],
+    [
+        "hiredis",
+        "hiredis",
+        "2.0.0"
+    ],
+    [
+        "aws-requests-auth",
+        "aws-requests-auth",
+        "0.4.3"
+    ],
+    [
+        "pyemd",
+        "pyemd",
+        "0.5.1"
+    ],
+    [
+        "jaeger-client",
+        "jaeger-client",
+        "4.8.0"
+    ],
+    [
+        "rhash",
+        "rhash",
+        "1.4.2"
+    ],
+    [
+        "cachecontrol",
+        "cachecontrol",
+        "0.12.6"
+    ],
+    [
+        "pandocfilters",
+        "pandocfilters",
+        "1.5.0"
+    ],
+    [
+        "trustme",
+        "trustme",
+        "0.9.0"
+    ],
+    [
+        "asyncpgsa",
+        "asyncpgsa",
+        "0.27.1"
+    ],
+    [
+        "setproctitle",
+        "setproctitle",
+        "1.2.2"
+    ],
+    [
+        "rasterstats",
+        "rasterstats",
+        "0.15.0"
+    ],
+    [
+        "stone",
+        "stone",
+        "3.2.1"
+    ],
+    [
+        "dash-bio",
+        "dash-bio",
+        "0.7.1"
+    ],
+    [
+        "cornice",
+        "cornice",
+        "6.0.0"
+    ],
+    [
+        "flask-babel",
+        "flask-babel",
+        "2.0.0"
+    ],
+    [
+        "jupyter_kernel_gateway",
+        "jupyter_kernel_gateway",
+        "2.5.1"
+    ],
+    [
+        "pyamg",
+        "pyamg",
+        "4.1.0"
+    ],
+    [
+        "cattrs",
+        "cattrs",
+        "1.8.0"
+    ],
+    [
+        "inflection",
+        "inflection",
+        "0.5.1"
+    ],
+    [
+        "sacremoses",
+        "sacremoses",
+        "0.0.46"
+    ],
+    [
+        "astroid",
+        "astroid",
+        "2.8.0"
+    ],
+    [
+        "async-timeout",
+        "async-timeout",
+        "4.0.0a3"
+    ],
+    [
+        "w3lib",
+        "w3lib",
+        "1.22.0"
+    ],
+    [
+        "recommonmark",
+        "recommonmark",
+        "0.7.1"
+    ],
+    [
+        "filelock",
+        "filelock",
+        "3.3.0"
+    ],
+    [
+        "pep8-naming",
+        "pep8-naming",
+        "0.12.1"
+    ],
+    [
+        "tomlkit",
+        "tomlkit",
+        "0.7.2"
+    ],
+    [
+        "billiard",
+        "billiard",
+        "3.6.4.0"
+    ],
+    [
+        "confuse",
+        "confuse",
+        "1.6.0"
+    ],
+    [
+        "setuptools-rust",
+        "setuptools-rust",
+        "0.12.1"
+    ],
+    [
+        "packaging",
+        "packaging",
+        "21.0"
+    ],
+    [
+        "django-pyodbc-azure",
+        "django-pyodbc-azure",
+        "2.1.0.0"
+    ],
+    [
+        "jsonpatch",
+        "jsonpatch",
+        "1.32"
+    ],
+    [
+        "gmpy2",
+        "gmpy2",
+        "2.1.0rc1"
+    ],
+    [
+        "atom",
+        "atom",
+        "0.7.0rc0"
+    ],
+    [
+        "dicttoxml",
+        "dicttoxml",
+        "1.7.4"
+    ],
+    [
+        "promise",
+        "promise",
+        "2.3"
+    ],
+    [
+        "vertica-python",
+        "vertica-python",
+        "1.0.1"
+    ],
+    [
+        "genson",
+        "genson",
+        "1.2.2"
+    ],
+    [
+        "h11",
+        "h11",
+        "0.12.0"
+    ],
+    [
+        "argh",
+        "argh",
+        "0.26.2"
+    ],
+    [
+        "traits",
+        "traits",
+        "6.2.0"
+    ],
+    [
+        "multiprocess",
+        "multiprocess",
+        "0.70.12.2"
+    ],
+    [
+        "ecos",
+        "ecos",
+        "2.0.7.post1"
+    ],
+    [
+        "fsspec",
+        "fsspec",
+        "2021.10.0"
+    ],
+    [
+        "yt",
+        "yt",
+        "4.0.1"
+    ],
+    [
+        "pg8000",
+        "pg8000",
+        "1.21.2"
+    ],
+    [
+        "nest-asyncio",
+        "nest-asyncio",
+        "1.5.1"
+    ],
+    [
+        "argon2-cffi",
+        "argon2-cffi",
+        "21.1.0"
+    ],
+    [
+        "argon2_cffi",
+        "argon2_cffi",
+        "21.1.0"
+    ],
+    [
+        "terminado",
+        "terminado",
+        "0.12.1"
+    ],
+    [
+        "semver",
+        "semver",
+        "3.0.0.dev2"
+    ],
+    [
+        "asynctest",
+        "asynctest",
+        "0.13.0"
+    ],
+    [
+        "texttable",
+        "texttable",
+        "1.6.4"
+    ],
+    [
+        "flask-bcrypt",
+        "flask-bcrypt",
+        "0.7.1"
+    ],
+    [
+        "flaky",
+        "flaky",
+        "3.7.0"
+    ],
+    [
+        "re2",
+        "re2",
+        "0.2.24"
+    ],
+    [
+        "conda-pack",
+        "conda-pack",
+        "0.6.0"
+    ],
+    [
+        "elasticsearch-async",
+        "elasticsearch-async",
+        "6.2.0"
+    ],
+    [
+        "ijson",
+        "ijson",
+        "3.1.4"
+    ],
+    [
+        "ansi2html",
+        "ansi2html",
+        "1.6.0"
+    ],
+    [
+        "blosc",
+        "blosc",
+        "1.10.4"
+    ],
+    [
+        "saspy",
+        "saspy",
+        "3.7.5"
+    ],
+    [
+        "tornado-json",
+        "tornado-json",
+        "1.3.4"
+    ],
+    [
+        "zope",
+        "zope",
+        "5.3"
+    ],
+    [
+        "requests_ntlm",
+        "requests_ntlm",
+        "1.1.0"
+    ],
+    [
+        "jaydebeapi",
+        "jaydebeapi",
+        "1.2.3"
+    ],
+    [
+        "netifaces",
+        "netifaces",
+        "0.11.0"
+    ],
+    [
+        "scikit-build",
+        "scikit-build",
+        "0.12.0"
+    ],
+    [
+        "atomicwrites",
+        "atomicwrites",
+        "1.4.0"
+    ],
+    [
+        "aws-xray-sdk",
+        "aws-xray-sdk",
+        "2.8.0"
+    ],
+    [
+        "wheel",
+        "wheel",
+        "0.37.0"
+    ],
+    [
+        "chaco",
+        "chaco",
+        "5.0.0"
+    ],
+    [
+        "metakernel",
+        "metakernel",
+        "0.27.5"
+    ],
+    [
+        "conda-build",
+        "conda-build",
+        "2.1.5"
+    ],
+    [
+        "bandersnatch",
+        "bandersnatch",
+        "5.0.0"
+    ],
+    [
+        "amqp",
+        "amqp",
+        "5.0.6"
+    ],
+    [
+        "zstandard",
+        "zstandard",
+        "0.15.2"
+    ],
+    [
+        "configobj",
+        "configobj",
+        "5.0.6"
+    ],
+    [
+        "pytest-selenium",
+        "pytest-selenium",
+        "2.0.1"
+    ],
+    [
+        "multidict",
+        "multidict",
+        "5.2.0"
+    ],
+    [
+        "google-pasta",
+        "google-pasta",
+        "0.2.0"
+    ],
+    [
+        "traitsui",
+        "traitsui",
+        "7.2.1"
+    ],
+    [
+        "zope.interface",
+        "zope.interface",
+        "5.4.0"
+    ],
+    [
+        "wcwidth",
+        "wcwidth",
+        "0.2.5"
+    ],
+    [
+        "xxhash",
+        "xxhash",
+        "2.0.2"
+    ],
+    [
+        "flake8-import-order",
+        "flake8-import-order",
+        "0.18.1"
+    ],
+    [
+        "jupyter_client",
+        "jupyter_client",
+        "7.0.5"
+    ],
+    [
+        "cssselect",
+        "cssselect",
+        "1.1.0"
+    ],
+    [
+        "dask-searchcv",
+        "dask-searchcv",
+        "0.2.0"
+    ],
+    [
+        "plac",
+        "plac",
+        "1.3.3"
+    ],
+    [
+        "param",
+        "param",
+        "1.11.22a4"
+    ],
+    [
+        "defusedxml",
+        "defusedxml",
+        "0.7.1"
+    ],
+    [
+        "hyperlink",
+        "hyperlink",
+        "21.0.0"
+    ],
+    [
+        "tensorflow-estimator",
+        "tensorflow-estimator",
+        "2.6.0"
+    ],
+    [
+        "conllu",
+        "conllu",
+        "4.4.1"
+    ],
+    [
+        "scikit-learn-intelex",
+        "scikit-learn-intelex",
+        "2021.4.0"
+    ],
+    [
+        "daal4py",
+        "daal4py",
+        "2021.4.0"
+    ],
+    [
+        "requests-kerberos",
+        "requests-kerberos",
+        "0.12.0"
+    ],
+    [
+        "asn1crypto",
+        "asn1crypto",
+        "1.4.0"
+    ],
+    [
+        "srsly",
+        "srsly",
+        "2.4.1"
+    ],
+    [
+        "huggingface_hub",
+        "huggingface_hub",
+        "0.0.18"
+    ],
+    [
+        "freetype-py",
+        "freetype-py",
+        "2.2.0"
+    ],
+    [
+        "adal",
+        "adal",
+        "1.2.7"
+    ],
+    [
+        "commonmark",
+        "commonmark",
+        "0.9.1"
+    ],
+    [
+        "croniter",
+        "croniter",
+        "1.0.15"
+    ],
+    [
+        "colour",
+        "colour",
+        "0.1.5"
+    ],
+    [
+        "typing_inspect",
+        "typing_inspect",
+        "0.7.1"
+    ],
+    [
+        "pytest-rerunfailures",
+        "pytest-rerunfailures",
+        "10.2"
+    ],
+    [
+        "futures",
+        "futures",
+        "3.3.0"
+    ],
+    [
+        "queuelib",
+        "queuelib",
+        "1.6.2"
+    ],
+    [
+        "bytecode",
+        "bytecode",
+        "0.13.0"
+    ],
+    [
+        "semantic_version",
+        "semantic_version",
+        "2.8.5"
+    ],
+    [
+        "flask-openid",
+        "flask-openid",
+        "1.3.0"
+    ],
+    [
+        "pytimeparse",
+        "pytimeparse",
+        "1.1.8"
+    ],
+    [
+        "httpcore",
+        "httpcore",
+        "0.13.7"
+    ],
+    [
+        "fast-histogram",
+        "fast-histogram",
+        "0.10"
+    ],
+    [
+        "pytest-ordering",
+        "pytest-ordering",
+        "0.6"
+    ],
+    [
+        "testfixtures",
+        "testfixtures",
+        "6.18.3"
+    ],
+    [
+        "aiohttp-jinja2",
+        "aiohttp-jinja2",
+        "1.5"
+    ],
+    [
+        "pretend",
+        "pretend",
+        "1.0.9"
+    ],
+    [
+        "clickhouse-sqlalchemy",
+        "clickhouse-sqlalchemy",
+        "0.1.6"
+    ],
+    [
+        "pyobjc-core",
+        "pyobjc-core",
+        "8.0b1"
+    ],
+    [
+        "pyobjc-framework-fsevents",
+        "pyobjc-framework-fsevents",
+        "8.0b1"
+    ],
+    [
+        "pyobjc-framework-cocoa",
+        "pyobjc-framework-cocoa",
+        "8.0b1"
+    ],
+    [
+        "pims",
+        "pims",
+        "0.5"
+    ],
+    [
+        "toro",
+        "toro",
+        "1.0.1"
+    ],
+    [
+        "curtsies",
+        "curtsies",
+        "0.3.7"
+    ],
+    [
+        "py-cpuinfo",
+        "py-cpuinfo",
+        "8.0.0"
+    ],
+    [
+        "hidapi",
+        "hidapi",
+        "0.10.1"
+    ],
+    [
+        "comtypes",
+        "comtypes",
+        "1.1.10"
+    ],
+    [
+        "trollius",
+        "trollius",
+        "2.2.1"
+    ],
+    [
+        "pyasn1",
+        "pyasn1",
+        "0.4.8"
+    ],
+    [
+        "lazy-object-proxy",
+        "lazy-object-proxy",
+        "1.6.0"
+    ],
+    [
+        "iminuit",
+        "iminuit",
+        "2.8.3"
+    ],
+    [
+        "typed-ast",
+        "typed-ast",
+        "1.4.3"
+    ],
+    [
+        "nbserverproxy",
+        "nbserverproxy",
+        "0.8.8"
+    ],
+    [
+        "distro",
+        "distro",
+        "1.6.0"
+    ],
+    [
+        "visvis",
+        "visvis",
+        "1.13.0"
+    ],
+    [
+        "tifffile",
+        "tifffile",
+        "2021.8.30"
+    ],
+    [
+        "openapi-spec-validator",
+        "openapi-spec-validator",
+        "0.3.1"
+    ],
+    [
+        "python-gflags",
+        "python-gflags",
+        "3.1.2"
+    ],
+    [
+        "ipaddr",
+        "ipaddr",
+        "2.2.0"
+    ],
+    [
+        "send2trash",
+        "send2trash",
+        "1.8.1b0"
+    ],
+    [
+        "mgwr",
+        "mgwr",
+        "2.1.2"
+    ],
+    [
+        "aiohttp-cors",
+        "aiohttp-cors",
+        "0.7.0"
+    ],
+    [
+        "sas_kernel",
+        "sas_kernel",
+        "2.4.11"
+    ],
+    [
+        "threadpoolctl",
+        "threadpoolctl",
+        "3.0.0"
+    ],
+    [
+        "charset-normalizer",
+        "charset-normalizer",
+        "2.0.6"
+    ],
+    [
+        "hupper",
+        "hupper",
+        "1.10.3"
+    ],
+    [
+        "svn",
+        "svn",
+        "1.0.1"
+    ],
+    [
+        "lz4",
+        "lz4",
+        "3.1.3"
+    ],
+    [
+        "python-engineio",
+        "python-engineio",
+        "4.2.1"
+    ],
+    [
+        "jq",
+        "jq",
+        "1.2.1"
+    ],
+    [
+        "func_timeout",
+        "func_timeout",
+        "4.3.5"
+    ],
+    [
+        "codecov",
+        "codecov",
+        "2.1.12"
+    ],
+    [
+        "astunparse",
+        "astunparse",
+        "1.6.3"
+    ],
+    [
+        "click-repl",
+        "click-repl",
+        "0.2.0"
+    ],
+    [
+        "fasteners",
+        "fasteners",
+        "0.16.3"
+    ],
+    [
+        "py7zr",
+        "py7zr",
+        "0.16.1"
+    ],
+    [
+        "cloudant",
+        "cloudant",
+        "2.15.0"
+    ],
+    [
+        "subprocess32",
+        "subprocess32",
+        "3.5.4"
+    ],
+    [
+        "jupyterhub-ldapauthenticator",
+        "jupyterhub-ldapauthenticator",
+        "1.3.2"
+    ],
+    [
+        "idna",
+        "idna",
+        "3.2"
+    ],
+    [
+        "opentracing_instrumentation",
+        "opentracing_instrumentation",
+        "3.3.1"
+    ],
+    [
+        "portalocker",
+        "portalocker",
+        "2.3.2"
+    ],
+    [
+        "msgpack-numpy",
+        "msgpack-numpy",
+        "0.4.7.1"
+    ],
+    [
+        "jsonlines",
+        "jsonlines",
+        "2.0.0"
+    ],
+    [
+        "nbpresent",
+        "nbpresent",
+        "3.0.0"
+    ],
+    [
+        "pycosat",
+        "pycosat",
+        "0.6.3"
+    ],
+    [
+        "mako",
+        "mako",
+        "1.1.5"
+    ],
+    [
+        "olefile",
+        "olefile",
+        "0.47.dev4"
+    ],
+    [
+        "pyclipper",
+        "pyclipper",
+        "1.3.0"
+    ],
+    [
+        "dash-html-components",
+        "dash-html-components",
+        "2.0.0"
+    ],
+    [
+        "deprecated",
+        "deprecated",
+        "1.2.13"
+    ],
+    [
+        "poetry-core",
+        "poetry-core",
+        "1.1.0a6"
+    ],
+    [
+        "flex",
+        "flex",
+        "6.14.1"
+    ],
+    [
+        "workerpool",
+        "workerpool",
+        "0.9.4"
+    ],
+    [
+        "ptyprocess",
+        "ptyprocess",
+        "0.7.0"
+    ],
+    [
+        "fastcache",
+        "fastcache",
+        "1.1.0"
+    ],
+    [
+        "spaghetti",
+        "spaghetti",
+        "1.6.4"
+    ],
+    [
+        "lerc",
+        "lerc",
+        "0.1.0"
+    ],
+    [
+        "tzlocal",
+        "tzlocal",
+        "4.0b2"
+    ],
+    [
+        "pooch",
+        "pooch",
+        "1.5.1"
+    ],
+    [
+        "dask-image",
+        "dask-image",
+        "0.6.0"
+    ],
+    [
+        "chameleon",
+        "chameleon",
+        "3.9.1"
+    ],
+    [
+        "pyprof2calltree",
+        "pyprof2calltree",
+        "1.4.5"
+    ],
+    [
+        "gitdb2",
+        "gitdb2",
+        "4.0.2"
+    ],
+    [
+        "gitdb",
+        "gitdb",
+        "4.0.7"
+    ],
+    [
+        "cheroot",
+        "cheroot",
+        "8.5.2"
+    ],
+    [
+        "aioitertools",
+        "aioitertools",
+        "0.8.0"
+    ],
+    [
+        "hdfs3",
+        "hdfs3",
+        "0.3.1"
+    ],
+    [
+        "poyo",
+        "poyo",
+        "0.5.0"
+    ],
+    [
+        "s3transfer",
+        "s3transfer",
+        "0.5.0"
+    ],
+    [
+        "pycares",
+        "pycares",
+        "4.0.0"
+    ],
+    [
+        "pytoml",
+        "pytoml",
+        "0.1.21"
+    ],
+    [
+        "readme_renderer",
+        "readme_renderer",
+        "30.0"
+    ],
+    [
+        "esda",
+        "esda",
+        "2.4.1"
+    ],
+    [
+        "google-cloud-storage",
+        "google-cloud-storage",
+        "1.42.3"
+    ],
+    [
+        "python-oauth2",
+        "python-oauth2",
+        "1.1.1"
+    ],
+    [
+        "brotlicffi",
+        "brotlicffi",
+        "1.0.9.2"
+    ],
+    [
+        "brotlipy",
+        "brotlipy",
+        "0.7.0"
+    ],
+    [
+        "pykdtree",
+        "pykdtree",
+        "1.3.4"
+    ],
+    [
+        "tblib",
+        "tblib",
+        "1.7.0"
+    ],
+    [
+        "pystan",
+        "pystan",
+        "3.2.0"
+    ],
+    [
+        "portpicker",
+        "portpicker",
+        "1.5.0b1"
+    ],
+    [
+        "glob2",
+        "glob2",
+        "0.7"
+    ],
+    [
+        "gast",
+        "gast",
+        "0.5.2"
+    ],
+    [
+        "tiledb",
+        "tiledb",
+        "0.10.2"
+    ],
+    [
+        "vega_datasets",
+        "vega_datasets",
+        "0.9.0"
+    ],
+    [
+        "affine",
+        "affine",
+        "2.3.0"
+    ],
+    [
+        "multimethod",
+        "multimethod",
+        "1.6"
+    ],
+    [
+        "wurlitzer",
+        "wurlitzer",
+        "3.0.2"
+    ],
+    [
+        "binaryornot",
+        "binaryornot",
+        "0.4.4"
+    ],
+    [
+        "pytest-tornado",
+        "pytest-tornado",
+        "0.8.1"
+    ],
+    [
+        "pyaml",
+        "pyaml",
+        "21.8.3"
+    ],
+    [
+        "click-plugins",
+        "click-plugins",
+        "1.1.1"
+    ],
+    [
+        "python_http_client",
+        "python_http_client",
+        "3.3.3"
+    ],
+    [
+        "imagesize",
+        "imagesize",
+        "1.2.0"
+    ],
+    [
+        "soupsieve",
+        "soupsieve",
+        "2.2.1"
+    ],
+    [
+        "teradata",
+        "teradata",
+        "15.10.0.21"
+    ],
+    [
+        "htmlmin",
+        "htmlmin",
+        "0.1.12"
+    ],
+    [
+        "cctools",
+        "cctools",
+        "7.0.17"
+    ],
+    [
+        "pyjks",
+        "pyjks",
+        "20.0.0"
+    ],
+    [
+        "incremental",
+        "incremental",
+        "21.3.0"
+    ],
+    [
+        "binstar",
+        "binstar",
+        "0.11.0"
+    ],
+    [
+        "drmaa",
+        "drmaa",
+        "0.7.9"
+    ],
+    [
+        "win_unicode_console",
+        "win_unicode_console",
+        "0.5"
+    ],
+    [
+        "isodate",
+        "isodate",
+        "0.6.0"
+    ],
+    [
+        "pathy",
+        "pathy",
+        "0.6.0"
+    ],
+    [
+        "pysmi",
+        "pysmi",
+        "0.3.4"
+    ],
+    [
+        "catalogue",
+        "catalogue",
+        "2.0.6"
+    ],
+    [
+        "jsonpointer",
+        "jsonpointer",
+        "2.1"
+    ],
+    [
+        "pytest-flake8",
+        "pytest-flake8",
+        "1.0.7"
+    ],
+    [
+        "zstd",
+        "zstd",
+        "1.5.0.2"
+    ],
+    [
+        "ipaddress",
+        "ipaddress",
+        "1.0.23"
+    ],
+    [
+        "conda-env",
+        "conda-env",
+        "2.4.2"
+    ],
+    [
+        "shellingham",
+        "shellingham",
+        "1.4.0"
+    ],
+    [
+        "pytest-subtests",
+        "pytest-subtests",
+        "0.5.0"
+    ],
+    [
+        "libarchive",
+        "libarchive",
+        "0.4.7"
+    ],
+    [
+        "junit-xml",
+        "junit-xml",
+        "1.9"
+    ],
+    [
+        "intervals",
+        "intervals",
+        "0.9.2"
+    ],
+    [
+        "google-auth-oauthlib",
+        "google-auth-oauthlib",
+        "0.4.6"
+    ],
+    [
+        "json5",
+        "json5",
+        "0.9.6"
+    ],
+    [
+        "libtiff",
+        "libtiff",
+        "0.4.2"
+    ],
+    [
+        "testtools",
+        "testtools",
+        "2.5.0"
+    ],
+    [
+        "vine",
+        "vine",
+        "5.0.0"
+    ],
+    [
+        "mypy_extensions",
+        "mypy_extensions",
+        "0.4.3"
+    ],
+    [
+        "partd",
+        "partd",
+        "1.2.0"
+    ],
+    [
+        "pathspec",
+        "pathspec",
+        "0.9.0"
+    ],
+    [
+        "async_generator",
+        "async_generator",
+        "1.10"
+    ],
+    [
+        "pytest-timeout",
+        "pytest-timeout",
+        "1.4.2"
+    ],
+    [
+        "bsdiff4",
+        "bsdiff4",
+        "1.2.1"
+    ],
+    [
+        "pyface",
+        "pyface",
+        "7.3.0"
+    ],
+    [
+        "mapclassify",
+        "mapclassify",
+        "2.4.3"
+    ],
+    [
+        "patch",
+        "patch",
+        "1.16"
+    ],
+    [
+        "mockito",
+        "mockito",
+        "1.2.2"
+    ],
+    [
+        "jupyter_dashboards_bundlers",
+        "jupyter_dashboards_bundlers",
+        "0.9.1"
+    ],
+    [
+        "splot",
+        "splot",
+        "1.1.4"
+    ],
+    [
+        "asciitree",
+        "asciitree",
+        "0.3.3"
+    ],
+    [
+        "nbclient",
+        "nbclient",
+        "0.5.4"
+    ],
+    [
+        "dask-glm",
+        "dask-glm",
+        "0.2.0"
+    ],
+    [
+        "visions",
+        "visions",
+        "0.7.4"
+    ],
+    [
+        "mdp",
+        "mdp",
+        "3.6"
+    ],
+    [
+        "pyls-black",
+        "pyls-black",
+        "0.4.7"
+    ],
+    [
+        "gridmap",
+        "gridmap",
+        "0.15.0"
+    ],
+    [
+        "service_identity",
+        "service_identity",
+        "21.1.0"
+    ],
+    [
+        "sshpubkeys",
+        "sshpubkeys",
+        "3.3.1"
+    ],
+    [
+        "xmlsec",
+        "xmlsec",
+        "1.3.12"
+    ],
+    [
+        "pyramid_jinja2",
+        "pyramid_jinja2",
+        "2.8"
+    ],
+    [
+        "contextvars",
+        "contextvars",
+        "2.4"
+    ],
+    [
+        "tobler",
+        "tobler",
+        "0.8.2"
+    ],
+    [
+        "pytest-azurepipelines",
+        "pytest-azurepipelines",
+        "1.0.0rc5"
+    ],
+    [
+        "python-editor",
+        "python-editor",
+        "1.0.4"
+    ],
+    [
+        "geotiff",
+        "geotiff",
+        "0.2.3"
+    ],
+    [
+        "cmake",
+        "cmake",
+        "3.21.3"
+    ],
+    [
+        "requests-ftp",
+        "requests-ftp",
+        "0.3.1"
+    ],
+    [
+        "pylev",
+        "pylev",
+        "1.4.0"
+    ],
+    [
+        "tensorflow-metadata",
+        "tensorflow-metadata",
+        "1.2.0"
+    ],
+    [
+        "chromedriver-binary",
+        "chromedriver-binary",
+        "95.0.4638.17.0"
+    ],
+    [
+        "entrypoints",
+        "entrypoints",
+        "0.3"
+    ],
+    [
+        "python-jsonrpc-server",
+        "python-jsonrpc-server",
+        "0.4.0"
+    ],
+    [
+        "deprecation",
+        "deprecation",
+        "2.1.0"
+    ],
+    [
+        "clikit",
+        "clikit",
+        "0.6.2"
+    ],
+    [
+        "cppy",
+        "cppy",
+        "1.1.0"
+    ],
+    [
+        "pathlib2",
+        "pathlib2",
+        "2.3.6"
+    ],
+    [
+        "bash",
+        "bash",
+        "0.6"
+    ],
+    [
+        "preshed",
+        "preshed",
+        "3.0.5"
+    ],
+    [
+        "monotonic",
+        "monotonic",
+        "1.6"
+    ],
+    [
+        "numcodecs",
+        "numcodecs",
+        "0.9.1"
+    ],
+    [
+        "flask-compress",
+        "flask-compress",
+        "1.10.1"
+    ],
+    [
+        "requests-file",
+        "requests-file",
+        "1.5.1"
+    ],
+    [
+        "jinja2-time",
+        "jinja2-time",
+        "0.2.0"
+    ],
+    [
+        "macfsevents",
+        "macfsevents",
+        "0.8.1"
+    ],
+    [
+        "btrees",
+        "btrees",
+        "4.9.2"
+    ],
+    [
+        "whichcraft",
+        "whichcraft",
+        "0.6.1"
+    ],
+    [
+        "chest",
+        "chest",
+        "0.2.3"
+    ],
+    [
+        "envisage",
+        "envisage",
+        "6.0.1"
+    ],
+    [
+        "pytest-variables",
+        "pytest-variables",
+        "1.9.0"
+    ],
+    [
+        "orderedmultidict",
+        "orderedmultidict",
+        "1.0.1"
+    ],
+    [
+        "hdmedians",
+        "hdmedians",
+        "0.14.2"
+    ],
+    [
+        "nosexcover",
+        "nosexcover",
+        "1.0.11"
+    ],
+    [
+        "backports.lzma",
+        "backports.lzma",
+        "0.0.14"
+    ],
+    [
+        "cgroupspy",
+        "cgroupspy",
+        "0.2.1"
+    ],
+    [
+        "pytest-runner",
+        "pytest-runner",
+        "5.3.1"
+    ],
+    [
+        "sniffio",
+        "sniffio",
+        "1.2.0"
+    ],
+    [
+        "secretstorage",
+        "secretstorage",
+        "3.3.1"
+    ],
+    [
+        "pickleshare",
+        "pickleshare",
+        "0.7.5"
+    ],
+    [
+        "text-unidecode",
+        "text-unidecode",
+        "1.3"
+    ],
+    [
+        "hpack",
+        "hpack",
+        "4.0.0"
+    ],
+    [
+        "knit",
+        "knit",
+        "0.2.4"
+    ],
+    [
+        "transaction",
+        "transaction",
+        "3.0.1"
+    ],
+    [
+        "aenum",
+        "aenum",
+        "3.1.0"
+    ],
+    [
+        "javaobj-py3",
+        "javaobj-py3",
+        "0.4.3"
+    ],
+    [
+        "cycler",
+        "cycler",
+        "0.10.0"
+    ],
+    [
+        "giddy",
+        "giddy",
+        "2.3.3"
+    ],
+    [
+        "abstract-rendering",
+        "abstract-rendering",
+        "0.5.1"
+    ],
+    [
+        "pkgconfig",
+        "pkgconfig",
+        "1.5.5"
+    ],
+    [
+        "functools32",
+        "functools32",
+        "3.2.3.post2"
+    ],
+    [
+        "smmap2",
+        "smmap2",
+        "3.0.1"
+    ],
+    [
+        "pandoc",
+        "pandoc",
+        "2.0a4"
+    ],
+    [
+        "smmap",
+        "smmap",
+        "4.0.0"
+    ],
+    [
+        "python-utils",
+        "python-utils",
+        "2.5.6"
+    ],
+    [
+        "archspec",
+        "archspec",
+        "0.1.2"
+    ],
+    [
+        "qds-sdk",
+        "qds-sdk",
+        "1.16.1"
+    ],
+    [
+        "ffmpeg",
+        "ffmpeg",
+        "1.4"
+    ],
+    [
+        "pathtools",
+        "pathtools",
+        "0.1.2"
+    ],
+    [
+        "appnope",
+        "appnope",
+        "0.1.2"
+    ],
+    [
+        "pywinpty",
+        "pywinpty",
+        "1.1.4"
+    ],
+    [
+        "essential_generators",
+        "essential_generators",
+        "1.0"
+    ],
+    [
+        "macholib",
+        "macholib",
+        "1.15.2"
+    ],
+    [
+        "mkl_fft",
+        "mkl_fft",
+        "1.3.0"
+    ],
+    [
+        "ntlm-auth",
+        "ntlm-auth",
+        "1.5.0"
+    ],
+    [
+        "pyinstaller-hooks-contrib",
+        "pyinstaller-hooks-contrib",
+        "2021.3"
+    ],
+    [
+        "pastel",
+        "pastel",
+        "0.2.1"
+    ],
+    [
+        "glog",
+        "glog",
+        "0.3.1"
+    ],
+    [
+        "argparse",
+        "argparse",
+        "1.4.0"
+    ],
+    [
+        "winkerberos",
+        "winkerberos",
+        "0.8.0"
+    ],
+    [
+        "chai",
+        "chai",
+        "1.1.2"
+    ],
+    [
+        "pytest-flakes",
+        "pytest-flakes",
+        "4.0.3"
+    ],
+    [
+        "beniget",
+        "beniget",
+        "0.4.1"
+    ],
+    [
+        "qstylizer",
+        "qstylizer",
+        "0.2.1"
+    ],
+    [
+        "cmarkgfm",
+        "cmarkgfm",
+        "0.6.0"
+    ],
+    [
+        "picklable-itertools",
+        "picklable-itertools",
+        "0.1.1"
+    ],
+    [
+        "persistent",
+        "persistent",
+        "4.7.0"
+    ],
+    [
+        "flask-json",
+        "flask-json",
+        "0.3.4"
+    ],
+    [
+        "intake-xarray",
+        "intake-xarray",
+        "0.5.0"
+    ],
+    [
+        "pytest-forked",
+        "pytest-forked",
+        "1.3.0"
+    ],
+    [
+        "pytest-metadata",
+        "pytest-metadata",
+        "1.11.0"
+    ],
+    [
+        "spint",
+        "spint",
+        "1.0.7"
+    ],
+    [
+        "pointpats",
+        "pointpats",
+        "2.2.0"
+    ],
+    [
+        "python3-openid",
+        "python3-openid",
+        "3.2.0"
+    ],
+    [
+        "zope.event",
+        "zope.event",
+        "4.5.0"
+    ],
+    [
+        "pytest-replay",
+        "pytest-replay",
+        "1.4.0"
+    ],
+    [
+        "cvxcanon",
+        "cvxcanon",
+        "0.1.2"
+    ],
+    [
+        "backports.functools_lru_cache",
+        "backports.functools_lru_cache",
+        "1.6.4"
+    ],
+    [
+        "google-api-core",
+        "google-api-core",
+        "2.0.1"
+    ],
+    [
+        "geos",
+        "geos",
+        "0.2.3"
+    ],
+    [
+        "google-resumable-media",
+        "google-resumable-media",
+        "2.0.3"
+    ],
+    [
+        "cligj",
+        "cligj",
+        "0.7.2"
+    ],
+    [
+        "hyperframe",
+        "hyperframe",
+        "6.0.1"
+    ],
+    [
+        "pure-sasl",
+        "pure-sasl",
+        "0.6.2"
+    ],
+    [
+        "pytest-doctestplus",
+        "pytest-doctestplus",
+        "0.11.0"
+    ],
+    [
+        "libpcap",
+        "libpcap",
+        "1.10.0b15"
+    ],
+    [
+        "pyasn1-modules",
+        "pyasn1-modules",
+        "0.2.8"
+    ],
+    [
+        "pamela",
+        "pamela",
+        "1.0.0"
+    ],
+    [
+        "zope.sqlalchemy",
+        "zope.sqlalchemy",
+        "1.6"
+    ],
+    [
+        "setuptools-git",
+        "setuptools-git",
+        "1.2"
+    ],
+    [
+        "epoxy",
+        "epoxy",
+        "1.0"
+    ],
+    [
+        "importlib-metadata",
+        "importlib-metadata",
+        "4.8.1"
+    ],
+    [
+        "importlib_metadata",
+        "importlib_metadata",
+        "4.8.1"
+    ],
+    [
+        "multi_key_dict",
+        "multi_key_dict",
+        "2.0.3"
+    ],
+    [
+        "cachy",
+        "cachy",
+        "0.3.0"
+    ],
+    [
+        "spreg",
+        "spreg",
+        "1.2.4"
+    ],
+    [
+        "extension-helpers",
+        "extension-helpers",
+        "0.1"
+    ],
+    [
+        "tranquilizer",
+        "tranquilizer",
+        "0.6.0"
+    ],
+    [
+        "jdcal",
+        "jdcal",
+        "1.4.1"
+    ],
+    [
+        "cookies",
+        "cookies",
+        "2.2.1"
+    ],
+    [
+        "vcversioner",
+        "vcversioner",
+        "2.16.0.0"
+    ],
+    [
+        "itemadapter",
+        "itemadapter",
+        "0.4.0"
+    ],
+    [
+        "pydata-google-auth",
+        "pydata-google-auth",
+        "1.2.0"
+    ],
+    [
+        "configparser",
+        "configparser",
+        "5.0.2"
+    ],
+    [
+        "unicodedata2",
+        "unicodedata2",
+        "13.0.0.post2"
+    ],
+    [
+        "zope.component",
+        "zope.component",
+        "5.0.1"
+    ],
+    [
+        "tzdata",
+        "tzdata",
+        "2021.2.post0"
+    ],
+    [
+        "priority",
+        "priority",
+        "2.0.0"
+    ],
+    [
+        "murmurhash",
+        "murmurhash",
+        "1.1.0.dev0"
+    ],
+    [
+        "webencodings",
+        "webencodings",
+        "0.5.1"
+    ],
+    [
+        "itemloaders",
+        "itemloaders",
+        "1.0.4"
+    ],
+    [
+        "pyutilib",
+        "pyutilib",
+        "6.0.0"
+    ],
+    [
+        "mkl-service",
+        "mkl-service",
+        "2.4.0"
+    ],
+    [
+        "git-lfs",
+        "git-lfs",
+        "1.6"
+    ],
+    [
+        "translationstring",
+        "translationstring",
+        "1.4"
+    ],
+    [
+        "iniconfig",
+        "iniconfig",
+        "1.1.1"
+    ],
+    [
+        "dbf",
+        "dbf",
+        "0.99.1"
+    ],
+    [
+        "outcome",
+        "outcome",
+        "1.1.0"
+    ],
+    [
+        "blz",
+        "blz",
+        "0.6.2"
+    ],
+    [
+        "spyder-kernels",
+        "spyder-kernels",
+        "2.1.3"
+    ],
+    [
+        "menuinst",
+        "menuinst",
+        "1.0.0"
+    ],
+    [
+        "pickle5",
+        "pickle5",
+        "0.0.11"
+    ],
+    [
+        "apipkg",
+        "apipkg",
+        "2.0.0"
+    ],
+    [
+        "pyramid_mako",
+        "pyramid_mako",
+        "1.1.0"
+    ],
+    [
+        "zipp",
+        "zipp",
+        "3.6.0"
+    ],
+    [
+        "testpath",
+        "testpath",
+        "0.5.0"
+    ],
+    [
+        "bkcharts",
+        "bkcharts",
+        "0.2"
+    ],
+    [
+        "win_inet_pton",
+        "win_inet_pton",
+        "1.1.0"
+    ],
+    [
+        "diff-match-patch",
+        "diff-match-patch",
+        "20200713"
+    ],
+    [
+        "imagecodecs",
+        "imagecodecs",
+        "2021.8.26"
+    ],
+    [
+        "glue-vispy-viewers",
+        "glue-vispy-viewers",
+        "1.0.3"
+    ],
+    [
+        "pyepsg",
+        "pyepsg",
+        "0.4.0"
+    ],
+    [
+        "nose-exclude",
+        "nose-exclude",
+        "0.5.0"
+    ],
+    [
+        "nodejs",
+        "nodejs",
+        "0.1.1"
+    ],
+    [
+        "polly",
+        "polly",
+        "0.6.4"
+    ],
+    [
+        "meld3",
+        "meld3",
+        "2.0.1"
+    ],
+    [
+        "d2to1",
+        "d2to1",
+        "0.2.12.post1"
+    ],
+    [
+        "constantly",
+        "constantly",
+        "15.1.0"
+    ],
+    [
+        "pytest-base-url",
+        "pytest-base-url",
+        "1.4.2"
+    ],
+    [
+        "sqlalchemy-jsonfield",
+        "sqlalchemy-jsonfield",
+        "1.0.0"
+    ],
+    [
+        "pyzstd",
+        "pyzstd",
+        "0.15.0"
+    ],
+    [
+        "thrift_sasl",
+        "thrift_sasl",
+        "0.4.3"
+    ],
+    [
+        "orange-canvas-core",
+        "orange-canvas-core",
+        "0.1.22"
+    ],
+    [
+        "ndg-httpsclient",
+        "ndg-httpsclient",
+        "0.5.1"
+    ],
+    [
+        "editables",
+        "editables",
+        "0.2"
+    ],
+    [
+        "setuptools_scm_git_archive",
+        "setuptools_scm_git_archive",
+        "1.1"
+    ],
+    [
+        "importlib_resources",
+        "importlib_resources",
+        "5.2.2"
+    ],
+    [
+        "twofish",
+        "twofish",
+        "0.3.0"
+    ],
+    [
+        "infinity",
+        "infinity",
+        "1.5"
+    ],
+    [
+        "setuptools-scm-git-archive",
+        "setuptools-scm-git-archive",
+        "1.1"
+    ],
+    [
+        "pytzdata",
+        "pytzdata",
+        "2020.1"
+    ],
+    [
+        "json-merge-patch",
+        "json-merge-patch",
+        "0.2"
+    ],
+    [
+        "keyrings.alt",
+        "keyrings.alt",
+        "4.1.0"
+    ],
+    [
+        "pyerfa",
+        "pyerfa",
+        "2.0.0"
+    ],
+    [
+        "mpg123",
+        "mpg123",
+        "0.4"
+    ],
+    [
+        "zc.lockfile",
+        "zc.lockfile",
+        "2.0"
+    ],
+    [
+        "snuggs",
+        "snuggs",
+        "1.4.7"
+    ],
+    [
+        "echo",
+        "echo",
+        "0.5"
+    ],
+    [
+        "pastedeploy",
+        "pastedeploy",
+        "2.1.1"
+    ],
+    [
+        "bz2file",
+        "bz2file",
+        "0.98"
+    ],
+    [
+        "pytest-csv",
+        "pytest-csv",
+        "3.0.0"
+    ],
+    [
+        "ruby",
+        "ruby",
+        "0.0.2"
+    ],
+    [
+        "libclang",
+        "libclang",
+        "12.0.0"
+    ],
+    [
+        "iso8601",
+        "iso8601",
+        "1.0.0a1"
+    ],
+    [
+        "fastrlock",
+        "fastrlock",
+        "0.6"
+    ],
+    [
+        "backcall",
+        "backcall",
+        "0.2.0"
+    ],
+    [
+        "fmt",
+        "fmt",
+        "0.3.1"
+    ],
+    [
+        "prison",
+        "prison",
+        "0.2.1"
+    ],
+    [
+        "spglm",
+        "spglm",
+        "1.0.8"
+    ],
+    [
+        "crashtest",
+        "crashtest",
+        "0.3.1"
+    ],
+    [
+        "swagger-ui-bundle",
+        "swagger-ui-bundle",
+        "0.0.9"
+    ],
+    [
+        "jsondate",
+        "jsondate",
+        "0.1.2"
+    ],
+    [
+        "slicerator",
+        "slicerator",
+        "1.0.0"
+    ],
+    [
+        "enscons",
+        "enscons",
+        "0.28.0"
+    ],
+    [
+        "mkl_random",
+        "mkl_random",
+        "1.2.2"
+    ],
+    [
+        "bison",
+        "bison",
+        "0.1.3"
+    ],
+    [
+        "pytest-arraydiff",
+        "pytest-arraydiff",
+        "0.3"
+    ],
+    [
+        "nbsmoke",
+        "nbsmoke",
+        "0.5.0"
+    ],
+    [
+        "rbc",
+        "rbc",
+        "0.2.0"
+    ],
+    [
+        "google-cloud-core",
+        "google-cloud-core",
+        "2.0.0"
+    ],
+    [
+        "maven",
+        "maven",
+        "0.1.0"
+    ],
+    [
+        "pytest-json",
+        "pytest-json",
+        "0.4.0"
+    ],
+    [
+        "locket",
+        "locket",
+        "0.2.1"
+    ],
+    [
+        "spvcm",
+        "spvcm",
+        "0.3.0"
+    ],
+    [
+        "jasper",
+        "jasper",
+        "0.1"
+    ],
+    [
+        "wsgiproxy2",
+        "wsgiproxy2",
+        "0.5.1"
+    ],
+    [
+        "tempora",
+        "tempora",
+        "4.1.1"
+    ],
+    [
+        "scramp",
+        "scramp",
+        "1.4.1"
+    ],
+    [
+        "idna_ssl",
+        "idna_ssl",
+        "1.1.0"
+    ],
+    [
+        "sasl",
+        "sasl",
+        "0.3.1"
+    ],
+    [
+        "backports.shutil_get_terminal_size",
+        "backports.shutil_get_terminal_size",
+        "1.0.0"
+    ],
+    [
+        "intake-parquet",
+        "intake-parquet",
+        "0.2.3"
+    ],
+    [
+        "case",
+        "case",
+        "1.5.3"
+    ],
+    [
+        "anyqt",
+        "anyqt",
+        "0.0.13"
+    ],
+    [
+        "jaraco.functools",
+        "jaraco.functools",
+        "3.3.0"
+    ],
+    [
+        "altgraph",
+        "altgraph",
+        "0.17.2"
+    ],
+    [
+        "vc",
+        "vc",
+        "2018.7.10"
+    ],
+    [
+        "libaio",
+        "libaio",
+        "0.8.3"
+    ],
+    [
+        "backports.weakref",
+        "backports.weakref",
+        "1.0.post1"
+    ],
+    [
+        "threadloop",
+        "threadloop",
+        "1.0.2"
+    ],
+    [
+        "vswhere",
+        "vswhere",
+        "1.3.0"
+    ],
+    [
+        "cmake_setuptools",
+        "cmake_setuptools",
+        "0.1.3"
+    ],
+    [
+        "docker-pycreds",
+        "docker-pycreds",
+        "0.4.0"
+    ],
+    [
+        "jaraco.text",
+        "jaraco.text",
+        "3.5.1"
+    ],
+    [
+        "grin",
+        "grin",
+        "1.3.0"
+    ],
+    [
+        "pytest-openfiles",
+        "pytest-openfiles",
+        "0.5.0"
+    ],
+    [
+        "clickhouse-cityhash",
+        "clickhouse-cityhash",
+        "1.0.2.3"
+    ],
+    [
+        "hstspreload",
+        "hstspreload",
+        "2021.10.1"
+    ],
+    [
+        "wstools",
+        "wstools",
+        "0.4.8"
+    ],
+    [
+        "backports_abc",
+        "backports_abc",
+        "0.5"
+    ],
+    [
+        "patch-ng",
+        "patch-ng",
+        "1.17.4"
+    ],
+    [
+        "certipy",
+        "certipy",
+        "0.1.3"
+    ],
+    [
+        "matplotlib-inline",
+        "matplotlib-inline",
+        "0.1.3"
+    ],
+    [
+        "googleapis-common-protos",
+        "googleapis-common-protos",
+        "1.53.0"
+    ],
+    [
+        "jaraco.collections",
+        "jaraco.collections",
+        "3.4.0"
+    ],
+    [
+        "jaraco.itertools",
+        "jaraco.itertools",
+        "6.0.3"
+    ],
+    [
+        "plaster_pastedeploy",
+        "plaster_pastedeploy",
+        "0.7"
+    ],
+    [
+        "google-crc32c",
+        "google-crc32c",
+        "1.2.0"
+    ],
+    [
+        "backports.tempfile",
+        "backports.tempfile",
+        "1.0"
+    ],
+    [
+        "db",
+        "db",
+        "0.1.1"
+    ],
+    [
+        "traceback2",
+        "traceback2",
+        "1.4.0"
+    ],
+    [
+        "xmlrpc2",
+        "xmlrpc2",
+        "0.3.1"
+    ],
+    [
+        "backports.os",
+        "backports.os",
+        "0.1.1"
+    ],
+    [
+        "intreehooks",
+        "intreehooks",
+        "1.0"
+    ],
+    [
+        "multivolumefile",
+        "multivolumefile",
+        "0.2.3"
+    ],
+    [
+        "tangled-up-in-unicode",
+        "tangled-up-in-unicode",
+        "0.2.0"
+    ],
+    [
+        "configurable-http-proxy",
+        "configurable-http-proxy",
+        "0.2.2"
+    ],
+    [
+        "python-hdfs",
+        "python-hdfs",
+        "0.4"
+    ],
+    [
+        "portend",
+        "portend",
+        "2.7.1"
+    ],
+    [
+        "pyct",
+        "pyct",
+        "0.4.8"
+    ],
+    [
+        "zope.deprecation",
+        "zope.deprecation",
+        "4.4.0"
+    ],
+    [
+        "mongo-tools",
+        "mongo-tools",
+        "0.0.2"
+    ],
+    [
+        "serverfiles",
+        "serverfiles",
+        "0.3.1"
+    ],
+    [
+        "pytest-astropy",
+        "pytest-astropy",
+        "0.9.0"
+    ],
+    [
+        "krb5",
+        "krb5",
+        "0.1.1"
+    ],
+    [
+        "jaraco.classes",
+        "jaraco.classes",
+        "3.2.1"
+    ],
+    [
+        "bottlechest",
+        "bottlechest",
+        "0.7.1"
+    ],
+    [
+        "pywin32-ctypes",
+        "pywin32-ctypes",
+        "0.2.0"
+    ],
+    [
+        "clyent",
+        "clyent",
+        "1.2.1"
+    ],
+    [
+        "yasm",
+        "yasm",
+        "0.1.0a1"
+    ],
+    [
+        "nasm",
+        "nasm",
+        "0.1.0"
+    ],
+    [
+        "mysql",
+        "mysql",
+        "0.0.3"
+    ],
+    [
+        "metis",
+        "metis",
+        "0.2a5"
+    ],
+    [
+        "pyppmd",
+        "pyppmd",
+        "0.17.0"
+    ],
+    [
+        "linecache2",
+        "linecache2",
+        "1.0.0"
+    ],
+    [
+        "assimp",
+        "assimp",
+        "4.1.4"
+    ],
+    [
+        "applaunchservices",
+        "applaunchservices",
+        "0.2.1"
+    ],
+    [
+        "cryptacular",
+        "cryptacular",
+        "1.6.2"
+    ],
+    [
+        "ghc",
+        "ghc",
+        "0.2.0"
+    ],
+    [
+        "readline",
+        "readline",
+        "6.2.4.1"
+    ],
+    [
+        "tini",
+        "tini",
+        "3.0.1"
+    ],
+    [
+        "proj",
+        "proj",
+        "0.2.0"
+    ],
+    [
+        "m4",
+        "m4",
+        "0.0.0"
+    ],
+    [
+        "gsl",
+        "gsl",
+        "0.0.3"
+    ],
+    [
+        "autoconf",
+        "autoconf",
+        "2021.9.12.1"
+    ],
+    [
+        "libuuid",
+        "libuuid",
+        "1.0.0"
+    ],
+    [
+        "gperf",
+        "gperf",
+        "0.2"
+    ],
+    [
+        "bcj-cffi",
+        "bcj-cffi",
+        "0.5.1"
+    ],
+    [
+        "wincertstore",
+        "wincertstore",
+        "0.2.1"
+    ],
+    [
+        "chainer",
+        "chainer",
+        "7.8.0"
+    ],
+    [
+        "sip",
+        "sip",
+        "6.2.0"
+    ],
+    [
+        "pycurl",
+        "pycurl",
+        "7.44.1"
+    ],
+    [
+        "lancet",
+        "lancet",
+        "0.8.1"
+    ],
+    [
+        "flake8-polyfill",
+        "flake8-polyfill",
+        "1.0.2"
+    ],
+    [
+        "dropbox",
+        "dropbox",
+        "11.21.0"
+    ],
+    [
+        "marshmallow-enum",
+        "marshmallow-enum",
+        "1.5.1"
+    ],
+    [
+        "tk",
+        "tk",
+        "0.1.0"
+    ],
+    [
+        "routes",
+        "routes",
+        "2.5.1"
+    ],
+    [
+        "repoze.lru",
+        "repoze.lru",
+        "0.7"
+    ],
+    [
+        "apache-libcloud",
+        "apache-libcloud",
+        "3.3.1"
+    ],
+    [
+        "qtconsole",
+        "qtconsole",
+        "5.1.1"
+    ],
+    [
+        "ipywidgets",
+        "ipywidgets",
+        "8.0.0a6"
+    ],
+    [
+        "thriftpy",
+        "thriftpy",
+        "0.3.9"
+    ],
+    [
+        "thrift",
+        "thrift",
+        "0.15.0"
+    ],
+    [
+        "holoviews",
+        "holoviews",
+        "1.14.6"
+    ],
+    [
+        "ant",
+        "ant",
+        "0.1.0"
+    ],
+    [
+        "jupyterlab_server",
+        "jupyterlab_server",
+        "2.8.2"
+    ],
+    [
+        "beautifulsoup4",
+        "beautifulsoup4",
+        "4.10.0"
+    ],
+    [
+        "bs4",
+        "bs4",
+        "0.0.1"
+    ],
+    [
+        "serf",
+        "serf",
+        "0.3.1"
+    ],
+    [
+        "jpeg",
+        "jpeg",
+        "0.1.4"
+    ],
+    [
+        "atk",
+        "atk",
+        "0.1.3.8b0"
+    ],
+    [
+        "meson",
+        "meson",
+        "0.59.2"
+    ],
+    [
+        "glib",
+        "glib",
+        "1.0.0"
+    ],
+    [
+        "orc",
+        "orc",
+        "0.1.0"
+    ],
+    [
+        "tapi",
+        "tapi",
+        "0.1.7"
+    ],
+    [
+        "clang",
+        "clang",
+        "11.0"
+    ],
+    [
+        "xlutils",
+        "xlutils",
+        "2.0.0"
+    ],
+    [
+        "whoosh",
+        "whoosh",
+        "2.7.4"
+    ],
+    [
+        "starkbank-ecdsa",
+        "starkbank-ecdsa",
+        "1.1.1"
+    ],
+    [
+        "sphinxcontrib-websupport",
+        "sphinxcontrib-websupport",
+        "1.2.4"
+    ],
+    [
+        "sgmllib3k",
+        "sgmllib3k",
+        "1.0.0"
+    ],
+    [
+        "segregation",
+        "segregation",
+        "2.1.0"
+    ],
+    [
+        "sas7bdat",
+        "sas7bdat",
+        "2.2.3"
+    ],
+    [
+        "rfc3986",
+        "rfc3986",
+        "1.5.0"
+    ],
+    [
+        "requests-toolbelt",
+        "requests-toolbelt",
+        "0.9.1"
+    ],
+    [
+        "pysal",
+        "pysal",
+        "2.5.0"
+    ],
+    [
+        "python-jenkins",
+        "python-jenkins",
+        "1.8.0.0a0"
+    ],
+    [
+        "pysmbclient",
+        "pysmbclient",
+        "0.1.5"
+    ],
+    [
+        "pysftp",
+        "pysftp",
+        "0.2.9"
+    ],
+    [
+        "pyqtgraph",
+        "pyqtgraph",
+        "0.12.2"
+    ],
+    [
+        "pydotplus",
+        "pydotplus",
+        "2.0.2"
+    ],
+    [
+        "pathlib",
+        "pathlib",
+        "1.0.1"
+    ],
+    [
+        "passlib",
+        "passlib",
+        "1.7.4"
+    ],
+    [
+        "orange-widget-base",
+        "orange-widget-base",
+        "4.14.1"
+    ],
+    [
+        "palettable",
+        "palettable",
+        "3.3.0"
+    ],
+    [
+        "munkres",
+        "munkres",
+        "1.1.4"
+    ],
+    [
+        "mando",
+        "mando",
+        "0.7.0"
+    ],
+    [
+        "locustio",
+        "locustio",
+        "1.0b2"
+    ],
+    [
+        "jupyter_telemetry",
+        "jupyter_telemetry",
+        "0.1.0"
+    ],
+    [
+        "inequality",
+        "inequality",
+        "1.0.0"
+    ],
+    [
+        "hsluv",
+        "hsluv",
+        "5.0.2"
+    ],
+    [
+        "heapdict",
+        "heapdict",
+        "1.0.1"
+    ],
+    [
+        "geopandas",
+        "geopandas",
+        "0.10.0"
+    ],
+    [
+        "descartes",
+        "descartes",
+        "1.1.0"
+    ],
+    [
+        "dbfread",
+        "dbfread",
+        "2.0.7"
+    ],
+    [
+        "dash-table",
+        "dash-table",
+        "5.0.0"
+    ],
+    [
+        "cram",
+        "cram",
+        "0.7"
+    ],
+    [
+        "contextlib2",
+        "contextlib2",
+        "21.6.0"
+    ],
+    [
+        "clickclick",
+        "clickclick",
+        "20.10.2"
+    ],
+    [
+        "click-didyoumean",
+        "click-didyoumean",
+        "0.3.0"
+    ],
+    [
+        "ciocheck",
+        "ciocheck",
+        "0.1.1"
+    ],
+    [
+        "agate-excel",
+        "agate-excel",
+        "0.2.5"
+    ],
+    [
+        "agate-dbf",
+        "agate-dbf",
+        "0.2.2"
+    ],
+    [
+        "distributed",
+        "distributed",
+        "2021.9.1"
+    ],
+    [
+        "lit",
+        "lit",
+        "13.0.0rc1"
+    ],
+    [
+        "ipykernel",
+        "ipykernel",
+        "6.4.1"
+    ],
+    [
+        "astropy",
+        "astropy",
+        "4.3.1"
+    ],
+    [
+        "pytest-filter-subpackage",
+        "pytest-filter-subpackage",
+        "0.1.1"
+    ],
+    [
+        "singledispatch",
+        "singledispatch",
+        "3.7.0"
+    ],
+    [
+        "regex",
+        "regex",
+        "2021.9.30"
+    ],
+    [
+        "xlwings",
+        "xlwings",
+        "0.24.9"
+    ],
+    [
+        "pygments",
+        "pygments",
+        "2.10.0"
+    ],
+    [
+        "notebook",
+        "notebook",
+        "6.4.4"
+    ],
+    [
+        "debugpy",
+        "debugpy",
+        "1.5.0"
+    ],
+    [
+        "rust",
+        "rust",
+        "0.1.1"
+    ],
+    [
+        "jupyterlab",
+        "jupyterlab",
+        "4.0.0a11"
+    ],
+    [
+        "panel",
+        "panel",
+        "0.13.0a6"
+    ],
+    [
+        "pyzmq",
+        "pyzmq",
+        "22.3.0"
+    ],
+    [
+        "jupyter-packaging",
+        "jupyter-packaging",
+        "0.10.6"
+    ],
+    [
+        "jeepney",
+        "jeepney",
+        "0.7.1"
+    ],
+    [
+        "cffi",
+        "cffi",
+        "1.15.0rc1"
+    ],
+    [
+        "tbb4py",
+        "tbb4py",
+        "2021.4.0"
+    ],
+    [
+        "tbb-devel",
+        "tbb-devel",
+        "2021.4.0"
+    ],
+    [
+        "tbb",
+        "tbb",
+        "2021.4.0"
+    ],
+    [
+        "gdal",
+        "gdal",
+        "3.3.2"
+    ],
+    [
+        "hvplot",
+        "hvplot",
+        "0.8.0a7"
+    ],
+    [
+        "intel-opencl-rt",
+        "intel-opencl-rt",
+        "2021.4.0"
+    ],
+    [
+        "spdlog",
+        "spdlog",
+        "2.0.4"
+    ],
+    [
+        "intel-fortran-rt",
+        "intel-fortran-rt",
+        "2021.4.0"
+    ],
+    [
+        "intel-cmplr-lib-rt",
+        "intel-cmplr-lib-rt",
+        "2021.4.0"
+    ],
+    [
+        "intel-cmplr-lic-rt",
+        "intel-cmplr-lic-rt",
+        "2021.4.0"
+    ],
+    [
+        "dpcpp-cpp-rt",
+        "dpcpp-cpp-rt",
+        "2021.4.0"
+    ],
+    [
+        "oneccl-devel",
+        "oneccl-devel",
+        "2021.4.0"
+    ],
+    [
+        "python-kubernetes",
+        "python-kubernetes",
+        "0.2"
+    ],
+    [
+        "cython",
+        "cython",
+        "3.0.0a9"
+    ],
+    [
+        "eventlet",
+        "eventlet",
+        "0.32.0"
+    ],
+    [
+        "mkl",
+        "mkl",
+        "2021.4.0"
+    ],
+    [
+        "mkl-include",
+        "mkl-include",
+        "2021.4.0"
+    ],
+    [
+        "intel-openmp",
+        "intel-openmp",
+        "2021.4.0"
+    ],
+    [
+        "dal",
+        "dal",
+        "0.2"
+    ],
+    [
+        "daal-static",
+        "daal-static",
+        "2021.4.0"
+    ],
+    [
+        "daal-include",
+        "daal-include",
+        "2021.4.0"
+    ],
+    [
+        "daal-devel",
+        "daal-devel",
+        "2021.4.0"
+    ],
+    [
+        "daal",
+        "daal",
+        "2021.4.0"
+    ],
+    [
+        "geoalchemy2",
+        "geoalchemy2",
+        "0.9.4"
+    ],
+    [
+        "python-debian",
+        "python-debian",
+        "0.1.40"
+    ],
+    [
+        "proto-plus",
+        "proto-plus",
+        "1.19.2"
+    ],
+    [
+        "spyder",
+        "spyder",
+        "5.1.5"
+    ],
+    [
+        "pkginfo",
+        "pkginfo",
+        "1.7.1"
+    ],
+    [
+        "invoke",
+        "invoke",
+        "1.6.0"
+    ],
+    [
+        "glue-core",
+        "glue-core",
+        "1.2.2"
+    ],
+    [
+        "pytorch",
+        "pytorch",
+        "1.0.2"
+    ],
+    [
+        "libpysal",
+        "libpysal",
+        "4.5.1"
+    ],
+    [
+        "nuitka",
+        "nuitka",
+        "0.6.17.1"
+    ],
+    [
+        "go",
+        "go",
+        "1.2.1"
+    ],
+    [
+        "ephem",
+        "ephem",
+        "4.1"
+    ],
+    [
+        "anaconda-client",
+        "anaconda-client",
+        "1.2.2"
+    ],
+    [
+        "supervisor",
+        "supervisor",
+        "4.2.2"
+    ],
+    [
+        "sphinxcontrib-htmlhelp",
+        "sphinxcontrib-htmlhelp",
+        "2.0.0"
+    ],
+    [
+        "sphinxcontrib-serializinghtml",
+        "sphinxcontrib-serializinghtml",
+        "1.1.5"
+    ],
+    [
+        "sortedcontainers",
+        "sortedcontainers",
+        "2.4.0"
+    ],
+    [
+        "scikit-rf",
+        "scikit-rf",
+        "0.19.0"
+    ],
+    [
+        "mypy",
+        "mypy",
+        "0.910"
+    ],
+    [
+        "execnet",
+        "execnet",
+        "1.9.0"
+    ],
+    [
+        "pyviz_comms",
+        "pyviz_comms",
+        "2.1.0"
+    ],
+    [
+        "datashader",
+        "datashader",
+        "0.13.0"
+    ],
+    [
+        "humanfriendly",
+        "humanfriendly",
+        "10.0"
+    ],
+    [
+        "coloredlogs",
+        "coloredlogs",
+        "15.0.1"
+    ],
+    [
+        "pyarrow",
+        "pyarrow",
+        "5.0.0"
+    ],
+    [
+        "bazel",
+        "bazel",
+        "0.0.0.20200723"
+    ],
+    [
+        "tornado",
+        "tornado",
+        "6.1"
+    ],
+    [
+        "python-lmdb",
+        "python-lmdb",
+        "1.0.0b1"
+    ],
+    [
+        "leveldb",
+        "leveldb",
+        "0.201"
+    ],
+    [
+        "h5py",
+        "h5py",
+        "3.4.0"
+    ],
+    [
+        "gensim",
+        "gensim",
+        "4.1.2"
+    ],
+    [
+        "et_xmlfile",
+        "et_xmlfile",
+        "1.1.0"
+    ],
+    [
+        "docutils",
+        "docutils",
+        "0.17.1"
+    ],
+    [
+        "cftime",
+        "cftime",
+        "1.5.1"
+    ],
+    [
+        "anaconda",
+        "anaconda",
+        "0.0.1.1"
+    ],
+    [
+        "aiofiles",
+        "aiofiles",
+        "0.7.0"
+    ],
+    [
+        "dnspython",
+        "dnspython",
+        "2.1.0"
+    ],
+    [
+        "distlib",
+        "distlib",
+        "0.3.3"
+    ],
+    [
+        "radon",
+        "radon",
+        "5.1.0"
+    ],
+    [
+        "pytest-mpi",
+        "pytest-mpi",
+        "0.5"
+    ],
+    [
+        "phik",
+        "phik",
+        "0.12.0"
+    ],
+    [
+        "pbr",
+        "pbr",
+        "5.6.0"
+    ],
+    [
+        "owslib",
+        "owslib",
+        "0.25.0"
+    ],
+    [
+        "flake8",
+        "flake8",
+        "3.9.2"
+    ],
+    [
+        "babel",
+        "babel",
+        "2.9.1"
+    ],
+    [
+        "alembic",
+        "alembic",
+        "1.7.3"
+    ],
+    [
+        "reportlab",
+        "reportlab",
+        "3.6.1"
+    ],
+    [
+        "sympy",
+        "sympy",
+        "1.9rc1"
+    ],
+    [
+        "geoviews",
+        "geoviews",
+        "1.9.2"
+    ],
+    [
+        "zfpy",
+        "zfpy",
+        "0.5.5"
+    ],
+    [
+        "zfp",
+        "zfp",
+        "0.0.0"
+    ],
+    [
+        "enum34",
+        "enum34",
+        "1.1.10"
+    ],
+    [
+        "dash-renderer",
+        "dash-renderer",
+        "1.9.1"
+    ],
+    [
+        "agate-sql",
+        "agate-sql",
+        "0.5.8"
+    ],
+    [
+        "tinycss",
+        "tinycss",
+        "0.4"
+    ],
+    [
+        "conda-content-trust",
+        "conda-content-trust",
+        "0.1.2"
+    ],
+    [
+        "nbformat",
+        "nbformat",
+        "5.1.3"
+    ],
+    [
+        "pomegranate",
+        "pomegranate",
+        "0.14.5"
+    ],
+    [
+        "constructor",
+        "constructor",
+        "0.1.0"
+    ],
+    [
+        "jupyter_console",
+        "jupyter_console",
+        "6.4.0"
+    ],
+    [
+        "numba",
+        "numba",
+        "0.54.1rc1"
+    ],
+    [
+        "libmagic",
+        "libmagic",
+        "1.0"
+    ],
+    [
+        "lxml",
+        "lxml",
+        "4.6.3"
+    ],
+    [
+        "ipympl",
+        "ipympl",
+        "0.8.0"
+    ],
+    [
+        "ruamel_yaml",
+        "ruamel_yaml",
+        "0.17.16"
+    ],
+    [
+        "nbclassic",
+        "nbclassic",
+        "0.3.2"
+    ],
+    [
+        "pyinstaller",
+        "pyinstaller",
+        "4.5.1"
+    ],
+    [
+        "paste",
+        "paste",
+        "3.5.0"
+    ],
+    [
+        "jupyter_server",
+        "jupyter_server",
+        "1.11.1"
+    ],
+    [
+        "yapf",
+        "yapf",
+        "0.31.0"
+    ],
+    [
+        "pycodestyle",
+        "pycodestyle",
+        "2.7.0"
+    ],
+    [
+        "agate",
+        "agate",
+        "1.6.3"
+    ],
+    [
+        "openpyxl",
+        "openpyxl",
+        "3.0.9"
+    ],
+    [
+        "grpcio",
+        "grpcio",
+        "1.41.0"
+    ],
+    [
+        "websocket-client",
+        "websocket-client",
+        "1.2.1"
+    ],
+    [
+        "twisted-iocpsupport",
+        "twisted-iocpsupport",
+        "1.0.2"
+    ],
+    [
+        "unidecode",
+        "unidecode",
+        "1.3.2"
+    ],
+    [
+        "aniso8601",
+        "aniso8601",
+        "9.0.1"
+    ],
+    [
+        "waf",
+        "waf",
+        "0.0.1"
+    ],
+    [
+        "sputnik",
+        "sputnik",
+        "0.9.3"
+    ],
+    [
+        "pyopengl",
+        "pyopengl",
+        "3.1.5"
+    ],
+    [
+        "fuel",
+        "fuel",
+        "0.2.0"
+    ],
+    [
+        "cvxopt",
+        "cvxopt",
+        "1.2.7"
+    ],
+    [
+        "whisper",
+        "whisper",
+        "1.1.8"
+    ],
+    [
+        "scikit-bio",
+        "scikit-bio",
+        "0.5.6"
+    ],
+    [
+        "webob",
+        "webob",
+        "1.8.7"
+    ],
+    [
+        "rsa",
+        "rsa",
+        "4.7.2"
+    ],
+    [
+        "py4j",
+        "py4j",
+        "0.10.9.2"
+    ],
+    [
+        "tempita",
+        "tempita",
+        "0.5.3.dev0"
+    ],
+    [
+        "suds-jurko",
+        "suds-jurko",
+        "0.6"
+    ],
+    [
+        "pywget",
+        "pywget",
+        "0.31"
+    ],
+    [
+        "pycairo",
+        "pycairo",
+        "1.20.1"
+    ],
+    [
+        "neon",
+        "neon",
+        "0.1.2"
+    ],
+    [
+        "grpcio-tools",
+        "grpcio-tools",
+        "1.41.0"
+    ],
+    [
+        "gevent-websocket",
+        "gevent-websocket",
+        "0.10.1"
+    ],
+    [
+        "fabric",
+        "fabric",
+        "2.6.0"
+    ],
+    [
+        "cubes",
+        "cubes",
+        "1.1"
+    ],
+    [
+        "python-daemon",
+        "python-daemon",
+        "2.3.0"
+    ],
+    [
+        "capturer",
+        "capturer",
+        "3.0"
+    ],
+    [
+        "bpython",
+        "bpython",
+        "0.21"
+    ],
+    [
+        "verboselogs",
+        "verboselogs",
+        "1.7"
+    ],
+    [
+        "venusian",
+        "venusian",
+        "3.0.0"
+    ],
+    [
+        "testscenarios",
+        "testscenarios",
+        "0.5.0"
+    ],
+    [
+        "pbkdf2",
+        "pbkdf2",
+        "1.3"
+    ],
+    [
+        "pyls-spyder",
+        "pyls-spyder",
+        "0.4.0"
+    ],
+    [
+        "ruamel.yaml.clib",
+        "ruamel.yaml.clib",
+        "0.2.6"
+    ],
+    [
+        "ruamel.yaml",
+        "ruamel.yaml",
+        "0.17.16"
+    ],
+    [
+        "grpcio-gcp",
+        "grpcio-gcp",
+        "0.2.2"
+    ],
+    [
+        "flask-restful",
+        "flask-restful",
+        "0.3.9"
+    ],
+    [
+        "termcolor",
+        "termcolor",
+        "1.1.0"
+    ],
+    [
+        "plaster",
+        "plaster",
+        "1.0"
+    ],
+    [
+        "ipyparallel",
+        "ipyparallel",
+        "7.1.0"
+    ],
+    [
+        "grako",
+        "grako",
+        "3.99.9"
+    ],
+    [
+        "expressions",
+        "expressions",
+        "0.2.3"
+    ],
+    [
+        "blist",
+        "blist",
+        "1.3.6"
+    ],
+    [
+        "urwid",
+        "urwid",
+        "2.1.2"
+    ],
+    [
+        "pykerberos",
+        "pykerberos",
+        "1.2.3.dev0"
+    ],
+    [
+        "posix_ipc",
+        "posix_ipc",
+        "1.0.5"
+    ],
+    [
+        "joblib",
+        "joblib",
+        "1.0.1"
+    ],
+    [
+        "webtest",
+        "webtest",
+        "3.0.0"
+    ],
+    [
+        "pydispatcher",
+        "pydispatcher",
+        "2.0.5"
+    ],
+    [
+        "funcsigs",
+        "funcsigs",
+        "1.0.2"
+    ],
+    [
+        "datashape",
+        "datashape",
+        "0.5.2"
+    ],
+    [
+        "characteristic",
+        "characteristic",
+        "14.3.0"
+    ],
+    [
+        "backports.shutil_which",
+        "backports.shutil_which",
+        "3.5.2"
+    ],
+    [
+        "fontconfig",
+        "fontconfig",
+        "0.1"
+    ],
+    [
+        "zict",
+        "zict",
+        "2.0.0"
+    ],
+    [
+        "pytest-pep8",
+        "pytest-pep8",
+        "1.0.6"
+    ],
+    [
+        "pytest-localserver",
+        "pytest-localserver",
+        "0.5.0"
+    ],
+    [
+        "pytest-cache",
+        "pytest-cache",
+        "1.0"
+    ],
+    [
+        "pytest-astropy-header",
+        "pytest-astropy-header",
+        "0.1.2"
+    ],
+    [
+        "jupyterlab_pygments",
+        "jupyterlab_pygments",
+        "0.1.2"
+    ],
+    [
+        "msgpack-python",
+        "msgpack-python",
+        "0.5.6"
+    ],
+    [
+        "jupyter_core",
+        "jupyter_core",
+        "4.8.1"
+    ],
+    [
+        "wasabi",
+        "wasabi",
+        "0.8.2"
+    ],
+    [
+        "pytz",
+        "pytz",
+        "2021.3"
+    ],
+    [
+        "utf8proc",
+        "utf8proc",
+        "0.0.3"
+    ],
+    [
+        "sphinxcontrib-devhelp",
+        "sphinxcontrib-devhelp",
+        "1.0.2"
+    ],
+    [
+        "sphinxcontrib-qthelp",
+        "sphinxcontrib-qthelp",
+        "1.0.3"
+    ],
+    [
+        "sphinxcontrib-jsmath",
+        "sphinxcontrib-jsmath",
+        "1.0.1"
+    ],
+    [
+        "sphinxcontrib-applehelp",
+        "sphinxcontrib-applehelp",
+        "1.0.2"
+    ],
+    [
+        "alabaster",
+        "alabaster",
+        "0.7.12"
+    ],
+    [
+        "thriftpy2",
+        "thriftpy2",
+        "0.4.14"
+    ],
+    [
+        "lockfile",
+        "lockfile",
+        "0.12.2"
+    ],
+    [
+        "icu",
+        "icu",
+        "0.0.1"
+    ],
+    [
+        "docopt",
+        "docopt",
+        "0.6.2"
+    ],
+    [
+        "boost",
+        "boost",
+        "0.1"
+    ],
+    [
+        "blinker",
+        "blinker",
+        "1.4"
+    ],
+    [
+        "anyjson",
+        "anyjson",
+        "0.3.3"
+    ],
+    [
+        "appscript",
+        "appscript",
+        "1.1.2"
+    ],
+    [
+        "sortedcollections",
+        "sortedcollections",
+        "2.1.0"
+    ],
+    [
+        "dash",
+        "dash",
+        "2.0.0"
+    ],
+    [
+        "requests_download",
+        "requests_download",
+        "0.1.2"
+    ],
+    [
+        "apptools",
+        "apptools",
+        "5.1.0"
+    ],
+    [
+        "logilab-common",
+        "logilab-common",
+        "1.8.1"
+    ],
+    [
+        "pscript",
+        "pscript",
+        "0.7.6"
+    ],
+    [
+        "pycrypto",
+        "pycrypto",
+        "2.6.1"
+    ],
+    [
+        "neotime",
+        "neotime",
+        "1.7.4"
+    ],
+    [
+        "xlrd",
+        "xlrd",
+        "2.0.1"
+    ],
+    [
+        "pyopenssl",
+        "pyopenssl",
+        "21.0.0"
+    ],
+    [
+        "access",
+        "access",
+        "1.1.3"
+    ],
+    [
+        "py",
+        "py",
+        "1.10.0"
+    ],
+    [
+        "xlwt",
+        "xlwt",
+        "1.3.0"
+    ],
+    [
+        "widgetsnbextension",
+        "widgetsnbextension",
+        "4.0.0a4"
+    ],
+    [
+        "simplegeneric",
+        "simplegeneric",
+        "0.8.1"
+    ],
+    [
+        "pytest-remotedata",
+        "pytest-remotedata",
+        "0.3.2"
+    ],
+    [
+        "pep8",
+        "pep8",
+        "1.7.1"
+    ],
+    [
+        "jupyter",
+        "jupyter",
+        "1.0.0"
+    ],
+    [
+        "future",
+        "future",
+        "0.18.2"
+    ],
+    [
+        "unittest2",
+        "unittest2",
+        "1.1.0"
+    ],
+    [
+        "three-merge",
+        "three-merge",
+        "0.1.1"
+    ],
+    [
+        "mock",
+        "mock",
+        "4.0.3"
+    ],
+    [
+        "bidict",
+        "bidict",
+        "0.21.3"
+    ],
+    [
+        "libpython",
+        "libpython",
+        "0.2"
+    ],
+    [
+        "kombu",
+        "kombu",
+        "5.2.0rc1"
+    ],
+    [
+        "pyreadline",
+        "pyreadline",
+        "2.1"
+    ],
+    [
+        "ply",
+        "ply",
+        "3.11"
+    ],
+    [
+        "python.app",
+        "python.app",
+        "0.1"
+    ],
+    [
+        "pyaudio",
+        "pyaudio",
+        "0.2.11"
+    ],
+    [
+        "nose",
+        "nose",
+        "1.3.7"
+    ],
+    [
+        "ipython_genutils",
+        "ipython_genutils",
+        "0.2.0"
+    ],
+    [
+        "pexpect",
+        "pexpect",
+        "4.8.0"
+    ],
+    [
+        "editdistance",
+        "editdistance",
+        "0.5.3"
+    ],
+    [
+        "backports",
+        "backports",
+        "1.0"
+    ],
+    [
+        "gn",
+        "gn",
+        "0.1"
+    ],
+    [
+        "numpydoc",
+        "numpydoc",
+        "1.1.0"
+    ],
+    [
+        "pyxdg",
+        "pyxdg",
+        "0.27"
+    ],
+    [
+        "freetype",
+        "freetype",
+        "0"
+    ],
+    [
+        "statistics",
+        "statistics",
+        "3.4.0b3"
+    ],
+    [
+        "libtool",
+        "libtool",
+        "1.1.0"
+    ],
+    [
+        "jupyterlab_launcher",
+        "jupyterlab_launcher",
+        "0.13.1"
+    ],
+    [
+        "glueviz",
+        "glueviz",
+        "1.2.0"
+    ],
+    [
+        "sed",
+        "sed",
+        "0.3.1"
+    ],
+    [
+        "protego",
+        "protego",
+        "0.1.16"
+    ],
+    [
+        "basemap",
+        "basemap",
+        "1.0.7"
+    ],
+    [
+        "magma",
+        "magma",
+        "0.3.9"
+    ],
+    [
+        "leather",
+        "leather",
+        "0.3.3"
+    ],
+    [
+        "orange3",
+        "orange3",
+        "3.30.1"
+    ],
+    [
+        "baycomp",
+        "baycomp",
+        "1.0.2"
+    ],
+    [
+        "snappy",
+        "snappy",
+        "3.0.1"
+    ],
+    [
+        "yajl",
+        "yajl",
+        "0.3.5"
+    ],
+    [
+        "tensorboard-plugin-wit",
+        "tensorboard-plugin-wit",
+        "1.8.0"
+    ],
+    [
+        "pynio",
+        "pynio",
+        "1.0"
+    ],
+    [
+        "nss",
+        "nss",
+        "3.8.5"
+    ],
+    [
+        "llvmmath",
+        "llvmmath",
+        "0.1.1"
+    ],
+    [
+        "pil",
+        "pil",
+        "1.1.6"
+    ],
+    [
+        "vtk",
+        "vtk",
+        "9.1.0rc1"
+    ],
+    [
+        "casuarius",
+        "casuarius",
+        "1.1"
+    ],
+    [
+        "cdecimal",
+        "cdecimal",
+        "2.3"
+    ],
+    [
+        "mercurial",
+        "mercurial",
+        "5.9.1"
+    ],
+    [
+        "cheetah",
+        "cheetah",
+        "2.4.4"
+    ],
+    [
+        "vispy",
+        "vispy",
+        "0.9.0"
+    ],
+    [
+        "blaze",
+        "blaze",
+        "0.10.1"
+    ],
+    [
+        "mysql-connector-python",
+        "mysql-connector-python",
+        "8.0.26"
+    ],
+    [
+        "faulthandler",
+        "faulthandler",
+        "3.2"
+    ],
+    [
+        "python-ntlm",
+        "python-ntlm",
+        "1.1.0"
+    ],
+    [
+        "llvmpy",
+        "llvmpy",
+        "0.12.7"
+    ],
+    [
+        "pyopengl-accelerate",
+        "pyopengl-accelerate",
+        "3.1.5"
+    ],
+    [
+        "pykit",
+        "pykit",
+        "0.2.0"
+    ],
+    [
+        "pyramid_tm",
+        "pyramid_tm",
+        "2.4"
+    ],
+    [
+        "googlecl",
+        "googlecl",
+        "0.9.12"
+    ],
+    [
+        "blockspring",
+        "blockspring",
+        "0.1.13"
+    ],
+    [
+        "msvc_runtime",
+        "msvc_runtime",
+        "14.29.30133"
+    ],
+    [
+        "mayavi",
+        "mayavi",
+        "4.7.3"
+    ],
+    [
+        "make",
+        "make",
+        "0.1.6.post2"
+    ],
+    [
+        "mathjax",
+        "mathjax",
+        "0.1.2"
+    ],
+    [
+        "theano",
+        "theano",
+        "1.0.5"
+    ],
+    [
+        "gdata",
+        "gdata",
+        "2.0.18"
+    ],
+    [
+        "libssh2",
+        "libssh2",
+        "0.0.1"
+    ],
+    [
+        "gtest",
+        "gtest",
+        "0.1.5"
+    ],
+    [
+        "pyramid_debugtoolbar",
+        "pyramid_debugtoolbar",
+        "4.9"
+    ],
+    [
+        "mtq",
+        "mtq",
+        "0.6.0"
+    ],
+    [
+        "perl",
+        "perl",
+        "1.0.0"
+    ],
+    [
+        "wxpython",
+        "wxpython",
+        "4.1.1"
+    ],
+    [
+        "h2o",
+        "h2o",
+        "3.34.0.1"
+    ],
+    [
+        "hyde",
+        "hyde",
+        "0.8.9"
+    ],
+    [
+        "wget",
+        "wget",
+        "3.2"
+    ],
+    [
+        "zipfile36",
+        "zipfile36",
+        "0.1.3"
+    ],
+    [
+        "morfessor",
+        "morfessor",
+        "2.0.6"
+    ],
+    [
+        "dis3",
+        "dis3",
+        "0.1.3"
+    ],
+    [
+        "soappy",
+        "soappy",
+        "0.12.22"
+    ],
+    [
+        "quiver_engine",
+        "quiver_engine",
+        "0.1.4.1.4"
+    ],
+    [
+        "pygraphviz",
+        "pygraphviz",
+        "1.7"
+    ],
+    [
+        "wsgiref",
+        "wsgiref",
+        "0.1.2"
+    ],
+    [
+        "uriparser",
+        "uriparser",
+        "1.0"
+    ],
+    [
+        "dask-jobqueue",
+        "dask-jobqueue",
+        "0.7.3"
+    ],
+    [
+        "dash-core-components",
+        "dash-core-components",
+        "2.0.0"
+    ],
+    [
+        "mpi",
+        "mpi",
+        "1.0.0"
+    ],
+    [
+        "selectors2",
+        "selectors2",
+        "2.0.2"
+    ],
+    [
+        "libopencv",
+        "libopencv",
+        "0.0.1"
+    ],
+    [
+        "zip",
+        "zip",
+        "0.0.2"
+    ],
+    [
+        "pyo3-pack",
+        "pyo3-pack",
+        "0.7.0b12"
+    ],
+    [
+        "glpk",
+        "glpk",
+        "0.4.6"
+    ],
+    [
+        "icc_rt",
+        "icc_rt",
+        "2020.0.133"
+    ]
+]
\ No newline at end of file
diff --git a/tests/beast_data_preparation.py b/tests/beast_data_preparation.py
new file mode 100644
index 00000000..3aac7694
--- /dev/null
+++ b/tests/beast_data_preparation.py
@@ -0,0 +1,61 @@
+from pathlib import Path
+from packaging.version import Version as PkgVersion, InvalidVersion
+from typing import Optional, Union
+
+import json
+
+class Version(PkgVersion):
+    def __new__(cls, *args, **kwargs):
+        if args and isinstance(args[0], Version):
+            return args[0]
+        else:
+            return super(cls, Version).__new__(cls)
+
+    def __init__(self, versionobj: Optional[Union[PkgVersion, str]] = None, *, major=0, minor=0, patch=0):
+        if isinstance(versionobj, Version):
+            return
+        
+        if versionobj:
+            try:
+                super(Version, self).__init__(versionobj)
+            except InvalidVersion:
+                super(Version, self).__init__("0")
+            return
+
+        if major is None or minor is None or patch is None:
+            raise ValueError(
+                f"Either 'Version' must be initialized with either a string, packaging.version.Verson, {__class__.__qualname__}, or else keyword arguments for 'major', 'minor' and 'patch' must be provided. Actual invocation was: {__class__.__qualname__}({versionobj!r}, {major=!r}, {minor=!r})"
+            )
+
+        # string as only argument
+        # no way to construct a Version otherwise - WTF
+        versionobj = ".".join(map(str, (major, minor, patch)))
+        super(Version, self).__init__(versionobj)
+
+    def __iter__(self):
+        yield from self.release
+
+    def __repr__(self):
+        return f"Version('{super().__str__()}')"
+
+    def __hash__(self):
+        return hash(self._version)
+
+    @classmethod
+    def __get_validators__(cls):
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value):
+        return Version(value)
+
+p = (Path(__file__).parent.absolute() / "../integration/pypi.json")
+
+with open(p) as file:
+    data = json.load(file)["data"]
+    
+DATA = [(d["name"], d["name"], str(max(Version(v) for v in d["versions"]))) for d in data]
+
+p = (Path(__file__).parent.absolute() / "../beast_data.json").resolve()
+with open(p, "w") as file:
+    json.dump(DATA, file, indent=4, sort_keys=True)
diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py
new file mode 100644
index 00000000..c84c4562
--- /dev/null
+++ b/tests/integration/__init__.py
@@ -0,0 +1,25 @@
+import os, sys
+
+from pathlib import Path
+src = import_base = Path(__file__).parent / "../../src"
+cwd = Path().cwd()
+os.chdir(src)
+sys.path.insert(0, "") if "" not in sys.path else None
+
+if sys.version_info < (3, 9) and "use" not in sys.modules:
+    import gc, types, typing
+    from typing import _GenericAlias as GenericAlias
+    from abc import ABCMeta
+    from collections.abc import Callable
+    from types import CellType
+    for t in (list, dict, set, tuple, frozenset, ABCMeta, Callable, CellType):
+        r = gc.get_referents(t.__dict__)[0]
+        r.update(
+            {
+                "__class_getitem__": classmethod(GenericAlias),
+            }
+        )
+
+
+import use
+os.chdir(cwd)
\ No newline at end of file
diff --git a/tests/integration/integration_test.py b/tests/integration/integration_test.py
old mode 100755
new mode 100644
index 946ce01e..3332f39e
--- a/tests/integration/integration_test.py
+++ b/tests/integration/integration_test.py
@@ -4,7 +4,9 @@
 import sys
 import tempfile
 import warnings
+from collections.abc import Callable
 from contextlib import closing
+from importlib.machinery import SourceFileLoader
 from pathlib import Path
 from threading import _shutdown_locks
 
@@ -14,7 +16,11 @@
 
 if Path("src").is_dir():
     sys.path.insert(0, "") if "" not in sys.path else None
-    lpath, rpath = (sys.path[0 : sys.path.index("") + 1], sys.path[sys.path.index("") + 2 :])
+    lpath, rpath = (
+        sys.path[: sys.path.index("") + 1],
+        sys.path[sys.path.index("") + 2 :],
+    )
+
     try:
         sys.path.clear()
         sys.path.__iadd__(lpath + [os.path.join(os.getcwd(), "src")] + rpath)
@@ -27,25 +33,16 @@
 import use
 
 __package__ = "tests"
+from tests.unit_test import reuse, ScopedCwd
 
 import logging
 
 log = logging.getLogger(".".join((__package__, __name__)))
-log.setLevel(logging.DEBUG if "DEBUG" in os.environ else logging.NOTSET)
-
+log.setLevel(logging.DEBUG if use.config.debugging else logging.NOTSET)
 
-@pytest.fixture()
-def reuse():
-    # making a completely new one each time would take ages (_registry)
-    use._using = {}
-    use._aspects = {}
-    use._reloaders = {}
-    return use
 
-
-# FIXME: Figure out why these don't work
 params = [
-    # ("olefile", "0.46"),
+    # ("olefile", "0.46"), # Windows-only
     ("workerpool", "0.9.4"),
     ("fastcache", "1.1.0"),
     ("pytest-cov", "2.12.1"),
@@ -55,8 +52,8 @@ def reuse():
     ("wheel", "0.36.2"),
     ("icontract", "2.5.4"),
     ("tiledb", "0.9.5"),
-    # ("wurlitzer", "3.0.2"),
-    # ("cctools", "7.0.17"),
+    ("wurlitzer", "3.0.2"),
+    # ("cctools", "7.0.17"), # too slow, takes minutes to build
     ("clang", "9.0"),
 ]
 
@@ -73,6 +70,7 @@ def test_sample(reuse, name, version):
         return
     assert False, "Should raise ImportError: missing hashes."
 
+
 @pytest.mark.parametrize("name, version", (("numpy", "1.19.3"),))
 def test_86_numpy(reuse, name, version):
     use = reuse  # for the eval() later
@@ -85,7 +83,6 @@ def test_86_numpy(reuse, name, version):
     return mod  # for the redownload test
 
 
-@pytest.mark.skipif(True, reason="Needs investigation")
 def test_redownload_module(reuse):
     def inject_fault(*, path, **kwargs):
         log.info("fault_inject: deleting %s", path)
@@ -93,10 +90,10 @@ def inject_fault(*, path, **kwargs):
 
     assert test_86_numpy(reuse, "example-pypi-package/examplepy", "0.1.0")
     try:
-        reuse.config["fault_inject"] = inject_fault
+        reuse.fault_inject = inject_fault
         assert test_86_numpy(reuse, "example-pypi-package/examplepy", "0.1.0")
     finally:
-        del reuse.config["fault_inject"]
+        del reuse.fault_inject
     assert test_86_numpy(reuse, "example-pypi-package/examplepy", "0.1.0")
 
 
@@ -110,26 +107,49 @@ def wrapper(*args, **kwargs):
     return wrapper
 
 
-def test_aspectize(reuse):
+def test_aspectize_defaults(reuse):
     # baseline
-    mod = reuse(reuse.Path("simple_funcs.py"))
-    assert mod.two() == 2
+    srcdir = Path(__file__).parent.parent.parent
+    if "tests.simple_funcs" in sys.modules:
+        del sys.modules["tests.simple_funcs"]
+    with ScopedCwd(srcdir):
+        mod = reuse(reuse.Path("./tests/simple_funcs.py"), package_name="tests")
+        assert mod.two() == 2
 
-    # all functions, but not classes or methods
-    mod = reuse(reuse.Path("simple_funcs.py")) @ (reuse.isfunction, "", double_function)
-
-    assert mod.two() == 4
-    assert mod.three() == 6
-    inst = mod.Two()
-    assert inst() == 2
-    inst = mod.Three()
-    assert inst.three() == 3
 
+def test_aspectize_function_by_name(reuse):
     # functions with specific names only
-    mod = reuse(reuse.Path("simple_funcs.py")) @ (reuse.isfunction, "two", double_function)
-    assert mod.two() == 4
-    assert mod.three() == 3
-    assert reuse.ismethod
+    srcdir = Path(__file__).parent.parent.parent
+    if "tests.simple_funcs" in sys.modules:
+        del sys.modules["tests.simple_funcs"]
+    with ScopedCwd(srcdir):
+        mod = reuse(reuse.Path("./tests/simple_funcs.py"), package_name="tests") @ (
+            reuse.isfunction,
+            "two",
+            double_function,
+        )
+        assert mod.two() == 4
+        assert mod.three() == 3
+        assert reuse.ismethod
+
+
+def test_aspectize_all_functions(reuse):
+    # all functions, but not classes or methods
+    srcdir = Path(__file__).parent.parent.parent
+    if "tests.simple_funcs" in sys.modules:
+        del sys.modules["tests.simple_funcs"]
+    with ScopedCwd(srcdir):
+        mod = reuse(reuse.Path("./tests/simple_funcs.py"), package_name="tests") @ (
+            reuse.isfunction,
+            "",
+            double_function,
+        )
+        assert mod.two() == 4
+        assert mod.three() == 6
+        inst = mod.Two()
+        assert inst() == 2
+        inst = mod.Three()
+        assert inst.three() == 3
 
 
 def test_simple_url(reuse):
@@ -152,4 +172,42 @@ def test_simple_url(reuse):
                 mod = reuse(reuse.URL(foo_uri), initial_globals={"a": 42})
                 assert mod.test() == 42
     finally:
-        os.chdir(orig_cwd)
\ No newline at end of file
+        os.chdir(orig_cwd)
+
+
+def test_autoinstall_numpy_dual_version(reuse):
+    ver1, ver2 = "1.19.3", "1.19.5"
+    for ver in (ver1, ver2):
+        for k, v in list(sys.modules.items()):
+            if k == "numpy" or k.startswith("numpy."):
+                loader = getattr(v, "__loader__", None) or v.__spec__.loader
+                if isinstance(loader, SourceFileLoader):
+                    del sys.modules[k]
+        try:
+            mod = suggested_artifact(reuse, "numpy", version=ver)
+            assert mod
+            assert mod.__version__ == ver
+        except RuntimeError:
+            pass
+
+
+def test_autoinstall_protobuf(reuse):
+    ver = "3.19.1"
+    mod = suggested_artifact(reuse, "protobuf/google.protobuf", version=ver)
+    assert mod.__version__ == ver
+    assert mod.__name__ == "google.protobuf"
+    assert tuple(Path(mod.__file__).parts[-3:]) == ("google", "protobuf", "__init__.py")
+
+
+def suggested_artifact(reuse, *args, **kwargs):
+    reuse.pimp._clean_sys_modules(args[0].split("/")[-1].split(".")[0])
+    try:
+        mod = reuse(*args, modes=reuse.auto_install | reuse.Modes.fastfail, **kwargs)
+        return mod
+    except RuntimeWarning as rw:
+        last_line = str(rw).strip().splitlines()[-1].strip()
+        log.info("Usimg last line as suggested artifact: %s", repr(last_line))
+        last_line2 = last_line.replace("protobuf", "protobuf/google.protobuf")
+        mod = eval(last_line2)
+        log.info("suggest artifact returning: %s", mod)
+        return mod
diff --git a/tests/integration/test_pypi_model.py b/tests/integration/test_pypi_model.py
index 731451b1..43af1e96 100644
--- a/tests/integration/test_pypi_model.py
+++ b/tests/integration/test_pypi_model.py
@@ -3,7 +3,7 @@
 import traceback
 
 import requests
-from use.pypi_model import PyPI_Project
+from use.pydantics import PyPI_Project
 
 with open("pypi.json", "r") as f:
     packages = json.load(f)
diff --git a/tests/mass_test_initdata.json b/tests/mass_test_initdata.json
new file mode 100644
index 00000000..52ec796f
--- /dev/null
+++ b/tests/mass_test_initdata.json
@@ -0,0 +1,6607 @@
+[
+    [
+        "tensorflow",
+        "tensorflow",
+        "2.6.0"
+    ],
+    [
+        "tensorflow-gpu",
+        "tensorflow-gpu",
+        "2.6.0"
+    ],
+    [
+        "django",
+        "django",
+        "4.0a1"
+    ],
+    [
+        "flask",
+        "flask",
+        "2.0.2"
+    ],
+    [
+        "keras",
+        "keras",
+        "2.7.0rc0"
+    ],
+    [
+        "transformers",
+        "transformers",
+        "4.11.2"
+    ],
+    [
+        "protobuf",
+        "protobuf",
+        "4.0.0rc2"
+    ],
+    [
+        "scikit-learn",
+        "scikit-learn",
+        "1.0"
+    ],
+    [
+        "requests",
+        "requests",
+        "2.26.0"
+    ],
+    [
+        "scrapy",
+        "scrapy",
+        "2.5.0"
+    ],
+    [
+        "pandas",
+        "pandas",
+        "1.3.3"
+    ],
+    [
+        "pyspark",
+        "pyspark",
+        "3.1.2"
+    ],
+    [
+        "airflow",
+        "airflow",
+        "0.6"
+    ],
+    [
+        "black",
+        "black",
+        "21.9b0"
+    ],
+    [
+        "selenium",
+        "selenium",
+        "4.0.0rc2"
+    ],
+    [
+        "xgboost",
+        "xgboost",
+        "1.5.0rc1"
+    ],
+    [
+        "spacy",
+        "spacy",
+        "3.1.3"
+    ],
+    [
+        "mxnet",
+        "mxnet",
+        "2.0.0a0"
+    ],
+    [
+        "mxnet-mkl",
+        "mxnet-mkl",
+        "1.6.0"
+    ],
+    [
+        "tqdm",
+        "tqdm",
+        "4.62.3"
+    ],
+    [
+        "numpy",
+        "numpy",
+        "1.21.2"
+    ],
+    [
+        "celery",
+        "celery",
+        "5.2.0rc1"
+    ],
+    [
+        "flatbuffers",
+        "flatbuffers",
+        "2.0"
+    ],
+    [
+        "poetry",
+        "poetry",
+        "1.2.0a2"
+    ],
+    [
+        "bokeh",
+        "bokeh",
+        "2.4.0"
+    ],
+    [
+        "cookiecutter",
+        "cookiecutter",
+        "1.7.3"
+    ],
+    [
+        "luigi",
+        "luigi",
+        "3.0.3"
+    ],
+    [
+        "ipython",
+        "ipython",
+        "7.28.0"
+    ],
+    [
+        "matplotlib",
+        "matplotlib",
+        "3.5.0rc1"
+    ],
+    [
+        "faker",
+        "faker",
+        "8.16.0"
+    ],
+    [
+        "lightgbm",
+        "lightgbm",
+        "3.2.1"
+    ],
+    [
+        "imgaug",
+        "imgaug",
+        "0.4.0"
+    ],
+    [
+        "aiohttp",
+        "aiohttp",
+        "4.0.0a1"
+    ],
+    [
+        "click",
+        "click",
+        "8.0.1"
+    ],
+    [
+        "brotli",
+        "brotli",
+        "1.0.9"
+    ],
+    [
+        "plotly",
+        "plotly",
+        "5.3.1"
+    ],
+    [
+        "nltk",
+        "nltk",
+        "3.6.4"
+    ],
+    [
+        "datasets",
+        "datasets",
+        "1.12.1"
+    ],
+    [
+        "torchvision",
+        "torchvision",
+        "0.10.1"
+    ],
+    [
+        "pybind11",
+        "pybind11",
+        "2.8.0"
+    ],
+    [
+        "networkx",
+        "networkx",
+        "2.6.3"
+    ],
+    [
+        "redis",
+        "redis",
+        "3.5.3"
+    ],
+    [
+        "pillow",
+        "pillow",
+        "8.3.2"
+    ],
+    [
+        "dask",
+        "dask",
+        "2021.9.1"
+    ],
+    [
+        "seaborn",
+        "seaborn",
+        "0.11.2"
+    ],
+    [
+        "scipy",
+        "scipy",
+        "1.7.1"
+    ],
+    [
+        "aws-sam-translator",
+        "aws-sam-translator",
+        "1.39.0"
+    ],
+    [
+        "uvloop",
+        "uvloop",
+        "0.16.0"
+    ],
+    [
+        "pandas-profiling",
+        "pandas-profiling",
+        "3.1.0"
+    ],
+    [
+        "jinja2",
+        "jinja2",
+        "3.0.2"
+    ],
+    [
+        "gunicorn",
+        "gunicorn",
+        "20.1.0"
+    ],
+    [
+        "pytest",
+        "pytest",
+        "6.2.5"
+    ],
+    [
+        "httpx",
+        "httpx",
+        "1.0.0b0"
+    ],
+    [
+        "psutil",
+        "psutil",
+        "5.8.0"
+    ],
+    [
+        "pydantic",
+        "pydantic",
+        "1.8.2"
+    ],
+    [
+        "arrow",
+        "arrow",
+        "1.2.0"
+    ],
+    [
+        "pip",
+        "pip",
+        "21.2.4"
+    ],
+    [
+        "prompt_toolkit",
+        "prompt_toolkit",
+        "3.0.20"
+    ],
+    [
+        "prompt-toolkit",
+        "prompt-toolkit",
+        "3.0.20"
+    ],
+    [
+        "ninja",
+        "ninja",
+        "1.10.2.2"
+    ],
+    [
+        "paramiko",
+        "paramiko",
+        "2.7.2"
+    ],
+    [
+        "tensorboardx",
+        "tensorboardx",
+        "2.4"
+    ],
+    [
+        "altair",
+        "altair",
+        "4.1.0"
+    ],
+    [
+        "scapy",
+        "scapy",
+        "2.4.5"
+    ],
+    [
+        "boto3",
+        "boto3",
+        "1.18.54"
+    ],
+    [
+        "statsmodels",
+        "statsmodels",
+        "0.13.0"
+    ],
+    [
+        "pymysql",
+        "pymysql",
+        "1.0.2"
+    ],
+    [
+        "boto",
+        "boto",
+        "2.49.0"
+    ],
+    [
+        "modin",
+        "modin",
+        "0.11.0"
+    ],
+    [
+        "jupyterhub",
+        "jupyterhub",
+        "2.0.0b2"
+    ],
+    [
+        "typer",
+        "typer",
+        "0.4.0"
+    ],
+    [
+        "pymc3",
+        "pymc3",
+        "3.11.4"
+    ],
+    [
+        "pymc",
+        "pymc",
+        "2.3.8"
+    ],
+    [
+        "sh",
+        "sh",
+        "1.14.2"
+    ],
+    [
+        "werkzeug",
+        "werkzeug",
+        "2.0.1"
+    ],
+    [
+        "marshmallow",
+        "marshmallow",
+        "3.13.0"
+    ],
+    [
+        "tensorboard-data-server",
+        "tensorboard-data-server",
+        "0.6.1"
+    ],
+    [
+        "tensorboard",
+        "tensorboard",
+        "2.6.0"
+    ],
+    [
+        "tensorflow-tensorboard",
+        "tensorflow-tensorboard",
+        "1.5.1"
+    ],
+    [
+        "gevent",
+        "gevent",
+        "21.8.0"
+    ],
+    [
+        "docker-py",
+        "docker-py",
+        "1.10.6"
+    ],
+    [
+        "hypothesis",
+        "hypothesis",
+        "6.23.1"
+    ],
+    [
+        "cupy",
+        "cupy",
+        "10.0.0b3"
+    ],
+    [
+        "moto",
+        "moto",
+        "2.2.9.dev16"
+    ],
+    [
+        "asyncpg",
+        "asyncpg",
+        "0.24.0"
+    ],
+    [
+        "jedi",
+        "jedi",
+        "0.18.0"
+    ],
+    [
+        "watchdog",
+        "watchdog",
+        "2.1.6"
+    ],
+    [
+        "flower",
+        "flower",
+        "1.0.0"
+    ],
+    [
+        "flask-admin",
+        "flask-admin",
+        "1.5.8"
+    ],
+    [
+        "csvkit",
+        "csvkit",
+        "1.0.6"
+    ],
+    [
+        "pygithub",
+        "pygithub",
+        "1.55"
+    ],
+    [
+        "pendulum",
+        "pendulum",
+        "2.1.2"
+    ],
+    [
+        "xmltodict",
+        "xmltodict",
+        "0.12.0"
+    ],
+    [
+        "scikit-image",
+        "scikit-image",
+        "0.18.3"
+    ],
+    [
+        "flask-socketio",
+        "flask-socketio",
+        "5.1.1"
+    ],
+    [
+        "twisted",
+        "twisted",
+        "21.7.0"
+    ],
+    [
+        "cryptography",
+        "cryptography",
+        "35.0.0"
+    ],
+    [
+        "cryptography-vectors",
+        "cryptography-vectors",
+        "35.0.0"
+    ],
+    [
+        "conda",
+        "conda",
+        "4.3.16"
+    ],
+    [
+        "sqlalchemy",
+        "sqlalchemy",
+        "1.4.25"
+    ],
+    [
+        "trio",
+        "trio",
+        "0.19.0"
+    ],
+    [
+        "isort",
+        "isort",
+        "5.9.3"
+    ],
+    [
+        "sphinx",
+        "sphinx",
+        "4.2.0"
+    ],
+    [
+        "pyjwt",
+        "pyjwt",
+        "2.1.0"
+    ],
+    [
+        "virtualenv",
+        "virtualenv",
+        "20.8.1"
+    ],
+    [
+        "autopep8",
+        "autopep8",
+        "1.5.7"
+    ],
+    [
+        "sphinx_rtd_theme",
+        "sphinx_rtd_theme",
+        "1.0.0"
+    ],
+    [
+        "rapidjson",
+        "rapidjson",
+        "1.0.0"
+    ],
+    [
+        "connexion",
+        "connexion",
+        "2.9.0"
+    ],
+    [
+        "apscheduler",
+        "apscheduler",
+        "3.8.0"
+    ],
+    [
+        "curio",
+        "curio",
+        "1.5"
+    ],
+    [
+        "attrs",
+        "attrs",
+        "21.2.0"
+    ],
+    [
+        "toolz",
+        "toolz",
+        "0.11.1"
+    ],
+    [
+        "flask-sqlalchemy",
+        "flask-sqlalchemy",
+        "2.5.1"
+    ],
+    [
+        "pyramid",
+        "pyramid",
+        "2.0"
+    ],
+    [
+        "pylint",
+        "pylint",
+        "3.0.0a4"
+    ],
+    [
+        "flask-appbuilder",
+        "flask-appbuilder",
+        "3.3.3"
+    ],
+    [
+        "tensorflow-probability",
+        "tensorflow-probability",
+        "0.14.1"
+    ],
+    [
+        "ujson",
+        "ujson",
+        "4.2.0"
+    ],
+    [
+        "pymongo",
+        "pymongo",
+        "3.12.0"
+    ],
+    [
+        "elasticsearch",
+        "elasticsearch",
+        "7.15.0"
+    ],
+    [
+        "jsonschema",
+        "jsonschema",
+        "4.0.1"
+    ],
+    [
+        "elasticsearch-dsl",
+        "elasticsearch-dsl",
+        "7.4.0"
+    ],
+    [
+        "responses",
+        "responses",
+        "0.14.0"
+    ],
+    [
+        "pywin32",
+        "pywin32",
+        "301"
+    ],
+    [
+        "tenacity",
+        "tenacity",
+        "8.0.1"
+    ],
+    [
+        "slackclient",
+        "slackclient",
+        "2.9.3"
+    ],
+    [
+        "slack-sdk",
+        "slack-sdk",
+        "3.11.2"
+    ],
+    [
+        "gitpython",
+        "gitpython",
+        "3.1.24"
+    ],
+    [
+        "rise",
+        "rise",
+        "5.7.1"
+    ],
+    [
+        "memory_profiler",
+        "memory_profiler",
+        "0.58.0"
+    ],
+    [
+        "ftfy",
+        "ftfy",
+        "6.0.3"
+    ],
+    [
+        "freezegun",
+        "freezegun",
+        "1.1.0"
+    ],
+    [
+        "tensorflow-datasets",
+        "tensorflow-datasets",
+        "4.4.0"
+    ],
+    [
+        "tensorflow-hub",
+        "tensorflow-hub",
+        "0.12.0"
+    ],
+    [
+        "missingno",
+        "missingno",
+        "0.5.0"
+    ],
+    [
+        "flask-login",
+        "flask-login",
+        "0.5.0"
+    ],
+    [
+        "fonttools",
+        "fonttools",
+        "4.27.1"
+    ],
+    [
+        "biopython",
+        "biopython",
+        "1.79"
+    ],
+    [
+        "phonenumbers",
+        "phonenumbers",
+        "8.12.33"
+    ],
+    [
+        "urllib3",
+        "urllib3",
+        "1.26.7"
+    ],
+    [
+        "qgrid",
+        "qgrid",
+        "1.3.1"
+    ],
+    [
+        "authlib",
+        "authlib",
+        "1.0.0b1"
+    ],
+    [
+        "xlsxwriter",
+        "xlsxwriter",
+        "3.0.1"
+    ],
+    [
+        "markdown",
+        "markdown",
+        "3.3.4"
+    ],
+    [
+        "factory_boy",
+        "factory_boy",
+        "3.2.0"
+    ],
+    [
+        "colorama",
+        "colorama",
+        "0.4.4"
+    ],
+    [
+        "gpustat",
+        "gpustat",
+        "1.0.0b1"
+    ],
+    [
+        "sqlparse",
+        "sqlparse",
+        "0.4.2"
+    ],
+    [
+        "pyomniscidb",
+        "pyomniscidb",
+        "5.6.4.1"
+    ],
+    [
+        "python-socketio",
+        "python-socketio",
+        "5.4.0"
+    ],
+    [
+        "textdistance",
+        "textdistance",
+        "4.2.1"
+    ],
+    [
+        "shapely",
+        "shapely",
+        "1.8rc1"
+    ],
+    [
+        "pyserial",
+        "pyserial",
+        "3.5"
+    ],
+    [
+        "python-language-server",
+        "python-language-server",
+        "0.36.2"
+    ],
+    [
+        "prometheus_client",
+        "prometheus_client",
+        "0.11.0"
+    ],
+    [
+        "coremltools",
+        "coremltools",
+        "5.0"
+    ],
+    [
+        "thinc",
+        "thinc",
+        "8.0.10"
+    ],
+    [
+        "pycparser",
+        "pycparser",
+        "2.20"
+    ],
+    [
+        "word2vec",
+        "word2vec",
+        "0.11.1"
+    ],
+    [
+        "psycopg2",
+        "psycopg2",
+        "2.9.1"
+    ],
+    [
+        "jupyterlab_widgets",
+        "jupyterlab_widgets",
+        "2.0.0a3"
+    ],
+    [
+        "itsdangerous",
+        "itsdangerous",
+        "2.0.1"
+    ],
+    [
+        "oauthlib",
+        "oauthlib",
+        "3.1.1"
+    ],
+    [
+        "xarray",
+        "xarray",
+        "0.19.0"
+    ],
+    [
+        "smart_open",
+        "smart_open",
+        "5.2.1"
+    ],
+    [
+        "bleach",
+        "bleach",
+        "4.1.0"
+    ],
+    [
+        "markdown2",
+        "markdown2",
+        "2.4.1"
+    ],
+    [
+        "pyodbc",
+        "pyodbc",
+        "4.0.32"
+    ],
+    [
+        "azure-core",
+        "azure-core",
+        "1.19.0"
+    ],
+    [
+        "azure-cosmos",
+        "azure-cosmos",
+        "4.2.0"
+    ],
+    [
+        "azure",
+        "azure",
+        "5.0.0"
+    ],
+    [
+        "pyinotify",
+        "pyinotify",
+        "0.9.6"
+    ],
+    [
+        "more-itertools",
+        "more-itertools",
+        "8.10.0"
+    ],
+    [
+        "pandas-datareader",
+        "pandas-datareader",
+        "0.10.0"
+    ],
+    [
+        "imagehash",
+        "imagehash",
+        "4.2.1"
+    ],
+    [
+        "furl",
+        "furl",
+        "2.1.3"
+    ],
+    [
+        "vcrpy",
+        "vcrpy",
+        "4.1.1"
+    ],
+    [
+        "mpld3",
+        "mpld3",
+        "0.5.5"
+    ],
+    [
+        "pyquery",
+        "pyquery",
+        "1.4.3"
+    ],
+    [
+        "mistune",
+        "mistune",
+        "2.0.0rc1"
+    ],
+    [
+        "mysqlclient",
+        "mysqlclient",
+        "2.0.3"
+    ],
+    [
+        "aioredis",
+        "aioredis",
+        "2.0.0"
+    ],
+    [
+        "httpretty",
+        "httpretty",
+        "1.1.4"
+    ],
+    [
+        "qdarkstyle",
+        "qdarkstyle",
+        "3.0.2"
+    ],
+    [
+        "efficientnet",
+        "efficientnet",
+        "1.1.1"
+    ],
+    [
+        "dateparser",
+        "dateparser",
+        "1.1.0"
+    ],
+    [
+        "keras-applications",
+        "keras-applications",
+        "1.0.8"
+    ],
+    [
+        "requests-futures",
+        "requests-futures",
+        "1.0.0"
+    ],
+    [
+        "twython",
+        "twython",
+        "3.9.1"
+    ],
+    [
+        "absl-py",
+        "absl-py",
+        "0.14.1"
+    ],
+    [
+        "coverage",
+        "coverage",
+        "6.0"
+    ],
+    [
+        "retrying",
+        "retrying",
+        "1.3.3"
+    ],
+    [
+        "pycryptodomex",
+        "pycryptodomex",
+        "3.10.4"
+    ],
+    [
+        "pycryptodome",
+        "pycryptodome",
+        "3.10.4"
+    ],
+    [
+        "cfn-lint",
+        "cfn-lint",
+        "0.54.2"
+    ],
+    [
+        "python-dateutil",
+        "python-dateutil",
+        "2.8.2"
+    ],
+    [
+        "numexpr",
+        "numexpr",
+        "2.7.3"
+    ],
+    [
+        "yq",
+        "yq",
+        "2.12.2"
+    ],
+    [
+        "pythran",
+        "pythran",
+        "0.10.0"
+    ],
+    [
+        "chardet",
+        "chardet",
+        "4.0.0"
+    ],
+    [
+        "pyyaml",
+        "pyyaml",
+        "6.0b1"
+    ],
+    [
+        "ibis-framework",
+        "ibis-framework",
+        "1.4.0"
+    ],
+    [
+        "pyrsistent",
+        "pyrsistent",
+        "0.18.0"
+    ],
+    [
+        "rasterio",
+        "rasterio",
+        "1.2.9"
+    ],
+    [
+        "snakeviz",
+        "snakeviz",
+        "2.1.0"
+    ],
+    [
+        "wrapt",
+        "wrapt",
+        "1.13.0"
+    ],
+    [
+        "flit-core",
+        "flit-core",
+        "3.3.0"
+    ],
+    [
+        "flit",
+        "flit",
+        "3.3.0"
+    ],
+    [
+        "dill",
+        "dill",
+        "0.3.4"
+    ],
+    [
+        "xar",
+        "xar",
+        "20.12.2"
+    ],
+    [
+        "pyhive",
+        "pyhive",
+        "0.6.4"
+    ],
+    [
+        "cherrypy",
+        "cherrypy",
+        "18.6.1"
+    ],
+    [
+        "humanize",
+        "humanize",
+        "3.11.0"
+    ],
+    [
+        "setuptools",
+        "setuptools",
+        "58.2.0"
+    ],
+    [
+        "jmespath",
+        "jmespath",
+        "0.10.0"
+    ],
+    [
+        "jira",
+        "jira",
+        "3.0.1"
+    ],
+    [
+        "simplejson",
+        "simplejson",
+        "3.17.5"
+    ],
+    [
+        "requests-oauthlib",
+        "requests-oauthlib",
+        "1.3.0"
+    ],
+    [
+        "quantecon",
+        "quantecon",
+        "0.5.1"
+    ],
+    [
+        "flask-restx",
+        "flask-restx",
+        "0.5.1"
+    ],
+    [
+        "feedparser",
+        "feedparser",
+        "6.0.8"
+    ],
+    [
+        "greenlet",
+        "greenlet",
+        "1.1.2"
+    ],
+    [
+        "pefile",
+        "pefile",
+        "2021.9.3"
+    ],
+    [
+        "pyparsing",
+        "pyparsing",
+        "3.0.0rc2"
+    ],
+    [
+        "wtforms",
+        "wtforms",
+        "3.0.0a1"
+    ],
+    [
+        "cassandra-driver",
+        "cassandra-driver",
+        "3.25.0"
+    ],
+    [
+        "blessings",
+        "blessings",
+        "1.7"
+    ],
+    [
+        "scons",
+        "scons",
+        "4.2.0"
+    ],
+    [
+        "llvmlite",
+        "llvmlite",
+        "0.37.0"
+    ],
+    [
+        "pywavelets",
+        "pywavelets",
+        "1.1.1"
+    ],
+    [
+        "flask-jwt-extended",
+        "flask-jwt-extended",
+        "4.3.0"
+    ],
+    [
+        "sendgrid",
+        "sendgrid",
+        "6.8.2"
+    ],
+    [
+        "flask-wtf",
+        "flask-wtf",
+        "0.15.1"
+    ],
+    [
+        "rope",
+        "rope",
+        "0.20.1"
+    ],
+    [
+        "aiopg",
+        "aiopg",
+        "1.3.2b1"
+    ],
+    [
+        "nbconvert",
+        "nbconvert",
+        "6.2.0"
+    ],
+    [
+        "simple-salesforce",
+        "simple-salesforce",
+        "1.11.4"
+    ],
+    [
+        "pytest-mock",
+        "pytest-mock",
+        "3.6.1"
+    ],
+    [
+        "twine",
+        "twine",
+        "3.4.2"
+    ],
+    [
+        "quandl",
+        "quandl",
+        "3.6.1"
+    ],
+    [
+        "tabpy-server",
+        "tabpy-server",
+        "0.2"
+    ],
+    [
+        "tabpy-client",
+        "tabpy-client",
+        "0.2"
+    ],
+    [
+        "enaml",
+        "enaml",
+        "0.13.0"
+    ],
+    [
+        "pytest-cov",
+        "pytest-cov",
+        "3.0.0"
+    ],
+    [
+        "python-slugify",
+        "python-slugify",
+        "5.0.2"
+    ],
+    [
+        "doit",
+        "doit",
+        "0.33.1"
+    ],
+    [
+        "arviz",
+        "arviz",
+        "0.11.4"
+    ],
+    [
+        "ws4py",
+        "ws4py",
+        "0.5.1"
+    ],
+    [
+        "stripe",
+        "stripe",
+        "2.60.0"
+    ],
+    [
+        "cachetools",
+        "cachetools",
+        "4.2.4"
+    ],
+    [
+        "progress",
+        "progress",
+        "1.6"
+    ],
+    [
+        "quilt3",
+        "quilt3",
+        "3.5.0"
+    ],
+    [
+        "python-json-logger",
+        "python-json-logger",
+        "2.0.2"
+    ],
+    [
+        "pyflakes",
+        "pyflakes",
+        "2.3.1"
+    ],
+    [
+        "graphviz",
+        "graphviz",
+        "0.17"
+    ],
+    [
+        "pysocks",
+        "pysocks",
+        "1.7.1"
+    ],
+    [
+        "sparkmagic",
+        "sparkmagic",
+        "0.19.1"
+    ],
+    [
+        "hdijupyterutils",
+        "hdijupyterutils",
+        "0.19.1"
+    ],
+    [
+        "autovizwidget",
+        "autovizwidget",
+        "0.19.1"
+    ],
+    [
+        "pandasql",
+        "pandasql",
+        "0.7.3"
+    ],
+    [
+        "botocore",
+        "botocore",
+        "1.21.54"
+    ],
+    [
+        "cloudpickle",
+        "cloudpickle",
+        "2.0.0"
+    ],
+    [
+        "python-levenshtein",
+        "python-levenshtein",
+        "0.12.2"
+    ],
+    [
+        "typing_extensions",
+        "typing_extensions",
+        "3.10.0.2"
+    ],
+    [
+        "typing-extensions",
+        "typing-extensions",
+        "3.10.0.2"
+    ],
+    [
+        "typing",
+        "typing",
+        "3.10.0.0"
+    ],
+    [
+        "argcomplete",
+        "argcomplete",
+        "1.12.3"
+    ],
+    [
+        "keras-preprocessing",
+        "keras-preprocessing",
+        "1.1.2"
+    ],
+    [
+        "streamz",
+        "streamz",
+        "0.6.3"
+    ],
+    [
+        "waitress",
+        "waitress",
+        "2.0.0"
+    ],
+    [
+        "tabulate",
+        "tabulate",
+        "0.8.9"
+    ],
+    [
+        "path.py",
+        "path.py",
+        "12.5.0"
+    ],
+    [
+        "path",
+        "path",
+        "16.2.0"
+    ],
+    [
+        "jsonpickle",
+        "jsonpickle",
+        "2.0.0"
+    ],
+    [
+        "odo",
+        "odo",
+        "0.5.0"
+    ],
+    [
+        "python-jose",
+        "python-jose",
+        "3.3.0"
+    ],
+    [
+        "html5lib",
+        "html5lib",
+        "1.1"
+    ],
+    [
+        "bcolz",
+        "bcolz",
+        "1.2.1"
+    ],
+    [
+        "imageio",
+        "imageio",
+        "2.9.0"
+    ],
+    [
+        "cartopy",
+        "cartopy",
+        "0.20.0"
+    ],
+    [
+        "petl",
+        "petl",
+        "1.7.4"
+    ],
+    [
+        "keras-ocr",
+        "keras-ocr",
+        "0.8.7"
+    ],
+    [
+        "asgiref",
+        "asgiref",
+        "3.4.1"
+    ],
+    [
+        "line_profiler",
+        "line_profiler",
+        "3.3.1"
+    ],
+    [
+        "apispec",
+        "apispec",
+        "5.1.1"
+    ],
+    [
+        "opentsne",
+        "opentsne",
+        "0.6.0"
+    ],
+    [
+        "pywinrm",
+        "pywinrm",
+        "0.4.2"
+    ],
+    [
+        "sshtunnel",
+        "sshtunnel",
+        "0.4.0"
+    ],
+    [
+        "immutables",
+        "immutables",
+        "0.16"
+    ],
+    [
+        "pyshp",
+        "pyshp",
+        "2.1.3"
+    ],
+    [
+        "sockjs-tornado",
+        "sockjs-tornado",
+        "1.0.7"
+    ],
+    [
+        "fiona",
+        "fiona",
+        "1.8.20"
+    ],
+    [
+        "snakebite",
+        "snakebite",
+        "2.11.0"
+    ],
+    [
+        "sqlalchemy-utils",
+        "sqlalchemy-utils",
+        "0.37.8"
+    ],
+    [
+        "six",
+        "six",
+        "1.16.0"
+    ],
+    [
+        "flask-apscheduler",
+        "flask-apscheduler",
+        "1.12.2"
+    ],
+    [
+        "pydocstyle",
+        "pydocstyle",
+        "6.1.1"
+    ],
+    [
+        "pympler",
+        "pympler",
+        "0.9"
+    ],
+    [
+        "pynacl",
+        "pynacl",
+        "1.4.0"
+    ],
+    [
+        "toml",
+        "toml",
+        "0.10.2"
+    ],
+    [
+        "pytest-benchmark",
+        "pytest-benchmark",
+        "3.4.1"
+    ],
+    [
+        "cytoolz",
+        "cytoolz",
+        "0.11.0"
+    ],
+    [
+        "patsy",
+        "patsy",
+        "0.5.2"
+    ],
+    [
+        "appdirs",
+        "appdirs",
+        "1.4.4"
+    ],
+    [
+        "pytest-asyncio",
+        "pytest-asyncio",
+        "0.15.1"
+    ],
+    [
+        "bcrypt",
+        "bcrypt",
+        "3.2.0"
+    ],
+    [
+        "zarr",
+        "zarr",
+        "2.10.1"
+    ],
+    [
+        "h2",
+        "h2",
+        "4.0.0"
+    ],
+    [
+        "pytest-xdist",
+        "pytest-xdist",
+        "2.4.0"
+    ],
+    [
+        "cx_oracle",
+        "cx_oracle",
+        "8.2.1"
+    ],
+    [
+        "cleo",
+        "cleo",
+        "1.0.0a4"
+    ],
+    [
+        "dask-ml",
+        "dask-ml",
+        "1.9.0"
+    ],
+    [
+        "jpype1",
+        "jpype1",
+        "1.3.0"
+    ],
+    [
+        "opentracing",
+        "opentracing",
+        "2.4.0"
+    ],
+    [
+        "flask_cors",
+        "flask_cors",
+        "3.0.10"
+    ],
+    [
+        "flask-cors",
+        "flask-cors",
+        "3.0.10"
+    ],
+    [
+        "keyring",
+        "keyring",
+        "23.2.1"
+    ],
+    [
+        "libcst",
+        "libcst",
+        "0.3.21"
+    ],
+    [
+        "progressbar2",
+        "progressbar2",
+        "3.53.3"
+    ],
+    [
+        "pdoc3",
+        "pdoc3",
+        "0.10.0"
+    ],
+    [
+        "icalendar",
+        "icalendar",
+        "4.0.7"
+    ],
+    [
+        "fabric3",
+        "fabric3",
+        "1.14.post1"
+    ],
+    [
+        "colorlog",
+        "colorlog",
+        "6.4.1"
+    ],
+    [
+        "aiobotocore",
+        "aiobotocore",
+        "1.4.1"
+    ],
+    [
+        "ecdsa",
+        "ecdsa",
+        "0.18.0b1"
+    ],
+    [
+        "parsel",
+        "parsel",
+        "1.6.0"
+    ],
+    [
+        "python-crfsuite",
+        "python-crfsuite",
+        "0.9.7"
+    ],
+    [
+        "ldap3",
+        "ldap3",
+        "2.9.1"
+    ],
+    [
+        "yarl",
+        "yarl",
+        "1.6.3"
+    ],
+    [
+        "dm-tree",
+        "dm-tree",
+        "0.1.6"
+    ],
+    [
+        "clickhouse-driver",
+        "clickhouse-driver",
+        "0.2.2"
+    ],
+    [
+        "celery-redbeat",
+        "celery-redbeat",
+        "2.0.0"
+    ],
+    [
+        "pluggy",
+        "pluggy",
+        "1.0.0"
+    ],
+    [
+        "dictdiffer",
+        "dictdiffer",
+        "0.9.0"
+    ],
+    [
+        "pyproj",
+        "pyproj",
+        "3.2.1"
+    ],
+    [
+        "flask-caching",
+        "flask-caching",
+        "1.10.1"
+    ],
+    [
+        "python-louvain",
+        "python-louvain",
+        "0.15"
+    ],
+    [
+        "bottleneck",
+        "bottleneck",
+        "1.3.2"
+    ],
+    [
+        "impyla",
+        "impyla",
+        "0.18a1"
+    ],
+    [
+        "python-nvd3",
+        "python-nvd3",
+        "0.15.0"
+    ],
+    [
+        "mysql-python",
+        "mysql-python",
+        "1.2.5"
+    ],
+    [
+        "intake",
+        "intake",
+        "0.6.3"
+    ],
+    [
+        "parsedatetime",
+        "parsedatetime",
+        "2.6"
+    ],
+    [
+        "cached-property",
+        "cached-property",
+        "1.5.2"
+    ],
+    [
+        "decorator",
+        "decorator",
+        "5.1.0"
+    ],
+    [
+        "pymssql",
+        "pymssql",
+        "2.2.2"
+    ],
+    [
+        "nose-parameterized",
+        "nose-parameterized",
+        "0.6.0"
+    ],
+    [
+        "parameterized",
+        "parameterized",
+        "0.8.1"
+    ],
+    [
+        "astor",
+        "astor",
+        "0.8.1"
+    ],
+    [
+        "aredis",
+        "aredis",
+        "1.1.8"
+    ],
+    [
+        "unicodecsv",
+        "unicodecsv",
+        "0.14.1"
+    ],
+    [
+        "pydot",
+        "pydot",
+        "1.4.2"
+    ],
+    [
+        "opencensus-context",
+        "opencensus-context",
+        "0.2.dev0"
+    ],
+    [
+        "opencensus",
+        "opencensus",
+        "0.8.0"
+    ],
+    [
+        "multipledispatch",
+        "multipledispatch",
+        "0.6.0"
+    ],
+    [
+        "treelib",
+        "treelib",
+        "1.6.1"
+    ],
+    [
+        "pyhamcrest",
+        "pyhamcrest",
+        "2.0.2"
+    ],
+    [
+        "inflect",
+        "inflect",
+        "5.3.0"
+    ],
+    [
+        "mpmath",
+        "mpmath",
+        "1.2.1"
+    ],
+    [
+        "natsort",
+        "natsort",
+        "7.1.1"
+    ],
+    [
+        "pycryptosat",
+        "pycryptosat",
+        "0.7.0.dev3"
+    ],
+    [
+        "netcdf4",
+        "netcdf4",
+        "1.5.7"
+    ],
+    [
+        "configargparse",
+        "configargparse",
+        "1.5.3"
+    ],
+    [
+        "qtpy",
+        "qtpy",
+        "1.11.2"
+    ],
+    [
+        "validators",
+        "validators",
+        "0.18.2"
+    ],
+    [
+        "snowballstemmer",
+        "snowballstemmer",
+        "2.1.0"
+    ],
+    [
+        "fastparquet",
+        "fastparquet",
+        "0.7.1"
+    ],
+    [
+        "anyio",
+        "anyio",
+        "3.3.2"
+    ],
+    [
+        "munch",
+        "munch",
+        "2.5.1.dev12"
+    ],
+    [
+        "dataclasses",
+        "dataclasses",
+        "0.8"
+    ],
+    [
+        "kiwisolver",
+        "kiwisolver",
+        "1.3.2"
+    ],
+    [
+        "setuptools_scm",
+        "setuptools_scm",
+        "6.3.2"
+    ],
+    [
+        "setuptools-scm",
+        "setuptools-scm",
+        "6.3.2"
+    ],
+    [
+        "s3fs",
+        "s3fs",
+        "2021.10.0"
+    ],
+    [
+        "pynndescent",
+        "pynndescent",
+        "0.5.4"
+    ],
+    [
+        "statsd",
+        "statsd",
+        "3.3.0"
+    ],
+    [
+        "scandir",
+        "scandir",
+        "1.10.0"
+    ],
+    [
+        "pyte",
+        "pyte",
+        "0.8.0"
+    ],
+    [
+        "bitarray",
+        "bitarray",
+        "2.3.4"
+    ],
+    [
+        "mpc",
+        "mpc",
+        "0.0.4"
+    ],
+    [
+        "certifi",
+        "certifi",
+        "2021.5.30"
+    ],
+    [
+        "opt_einsum",
+        "opt_einsum",
+        "3.3.0"
+    ],
+    [
+        "colorcet",
+        "colorcet",
+        "2.0.6"
+    ],
+    [
+        "geventhttpclient",
+        "geventhttpclient",
+        "1.5.2"
+    ],
+    [
+        "datrie",
+        "datrie",
+        "0.8.2"
+    ],
+    [
+        "email-validator",
+        "email-validator",
+        "1.1.3"
+    ],
+    [
+        "datadog",
+        "datadog",
+        "0.42.0"
+    ],
+    [
+        "colorful",
+        "colorful",
+        "0.6.0a1"
+    ],
+    [
+        "coveralls",
+        "coveralls",
+        "3.2.0"
+    ],
+    [
+        "pypandoc",
+        "pypandoc",
+        "1.6.4"
+    ],
+    [
+        "fastavro",
+        "fastavro",
+        "1.4.5"
+    ],
+    [
+        "mpi4py",
+        "mpi4py",
+        "3.1.1"
+    ],
+    [
+        "lmdb",
+        "lmdb",
+        "1.2.1"
+    ],
+    [
+        "dlpack",
+        "dlpack",
+        "0.1"
+    ],
+    [
+        "pyperformance",
+        "pyperformance",
+        "1.0.2"
+    ],
+    [
+        "automat",
+        "automat",
+        "20.2.0"
+    ],
+    [
+        "jsondiff",
+        "jsondiff",
+        "1.3.0"
+    ],
+    [
+        "google-auth",
+        "google-auth",
+        "2.2.1"
+    ],
+    [
+        "marshmallow-sqlalchemy",
+        "marshmallow-sqlalchemy",
+        "0.26.1"
+    ],
+    [
+        "parso",
+        "parso",
+        "0.8.2"
+    ],
+    [
+        "python3-saml",
+        "python3-saml",
+        "1.12.0"
+    ],
+    [
+        "pivottablejs",
+        "pivottablejs",
+        "0.9.0"
+    ],
+    [
+        "traitlets",
+        "traitlets",
+        "5.1.0"
+    ],
+    [
+        "pytest-html",
+        "pytest-html",
+        "3.1.1"
+    ],
+    [
+        "flask-swagger",
+        "flask-swagger",
+        "0.2.14"
+    ],
+    [
+        "runipy",
+        "runipy",
+        "0.1.5"
+    ],
+    [
+        "intervaltree",
+        "intervaltree",
+        "3.1.0"
+    ],
+    [
+        "pydruid",
+        "pydruid",
+        "0.6.2"
+    ],
+    [
+        "pysnmp",
+        "pysnmp",
+        "4.4.12"
+    ],
+    [
+        "mccabe",
+        "mccabe",
+        "0.6.1"
+    ],
+    [
+        "mpl-scatter-density",
+        "mpl-scatter-density",
+        "0.7"
+    ],
+    [
+        "python-rapidjson",
+        "python-rapidjson",
+        "1.4"
+    ],
+    [
+        "markupsafe",
+        "markupsafe",
+        "2.0.1"
+    ],
+    [
+        "rtree",
+        "rtree",
+        "0.9.7"
+    ],
+    [
+        "python-memcached",
+        "python-memcached",
+        "1.59"
+    ],
+    [
+        "python-openid",
+        "python-openid",
+        "2.2.5"
+    ],
+    [
+        "colander",
+        "colander",
+        "1.8.3"
+    ],
+    [
+        "qtawesome",
+        "qtawesome",
+        "1.0.3"
+    ],
+    [
+        "python-snappy",
+        "python-snappy",
+        "0.6.0"
+    ],
+    [
+        "aiodns",
+        "aiodns",
+        "3.0.0"
+    ],
+    [
+        "cymem",
+        "cymem",
+        "2.0.5"
+    ],
+    [
+        "scp",
+        "scp",
+        "0.14.1"
+    ],
+    [
+        "pyperf",
+        "pyperf",
+        "2.3.0"
+    ],
+    [
+        "hiredis",
+        "hiredis",
+        "2.0.0"
+    ],
+    [
+        "aws-requests-auth",
+        "aws-requests-auth",
+        "0.4.3"
+    ],
+    [
+        "pyemd",
+        "pyemd",
+        "0.5.1"
+    ],
+    [
+        "jaeger-client",
+        "jaeger-client",
+        "4.8.0"
+    ],
+    [
+        "rhash",
+        "rhash",
+        "1.4.2"
+    ],
+    [
+        "cachecontrol",
+        "cachecontrol",
+        "0.12.6"
+    ],
+    [
+        "pandocfilters",
+        "pandocfilters",
+        "1.5.0"
+    ],
+    [
+        "trustme",
+        "trustme",
+        "0.9.0"
+    ],
+    [
+        "asyncpgsa",
+        "asyncpgsa",
+        "0.27.1"
+    ],
+    [
+        "setproctitle",
+        "setproctitle",
+        "1.2.2"
+    ],
+    [
+        "rasterstats",
+        "rasterstats",
+        "0.15.0"
+    ],
+    [
+        "stone",
+        "stone",
+        "3.2.1"
+    ],
+    [
+        "dash-bio",
+        "dash-bio",
+        "0.7.1"
+    ],
+    [
+        "cornice",
+        "cornice",
+        "6.0.0"
+    ],
+    [
+        "flask-babel",
+        "flask-babel",
+        "2.0.0"
+    ],
+    [
+        "jupyter_kernel_gateway",
+        "jupyter_kernel_gateway",
+        "2.5.1"
+    ],
+    [
+        "pyamg",
+        "pyamg",
+        "4.1.0"
+    ],
+    [
+        "cattrs",
+        "cattrs",
+        "1.8.0"
+    ],
+    [
+        "inflection",
+        "inflection",
+        "0.5.1"
+    ],
+    [
+        "sacremoses",
+        "sacremoses",
+        "0.0.46"
+    ],
+    [
+        "astroid",
+        "astroid",
+        "2.8.0"
+    ],
+    [
+        "async-timeout",
+        "async-timeout",
+        "4.0.0a3"
+    ],
+    [
+        "w3lib",
+        "w3lib",
+        "1.22.0"
+    ],
+    [
+        "recommonmark",
+        "recommonmark",
+        "0.7.1"
+    ],
+    [
+        "filelock",
+        "filelock",
+        "3.3.0"
+    ],
+    [
+        "pep8-naming",
+        "pep8-naming",
+        "0.12.1"
+    ],
+    [
+        "tomlkit",
+        "tomlkit",
+        "0.7.2"
+    ],
+    [
+        "billiard",
+        "billiard",
+        "3.6.4.0"
+    ],
+    [
+        "confuse",
+        "confuse",
+        "1.6.0"
+    ],
+    [
+        "setuptools-rust",
+        "setuptools-rust",
+        "0.12.1"
+    ],
+    [
+        "packaging",
+        "packaging",
+        "21.0"
+    ],
+    [
+        "django-pyodbc-azure",
+        "django-pyodbc-azure",
+        "2.1.0.0"
+    ],
+    [
+        "jsonpatch",
+        "jsonpatch",
+        "1.32"
+    ],
+    [
+        "gmpy2",
+        "gmpy2",
+        "2.1.0rc1"
+    ],
+    [
+        "atom",
+        "atom",
+        "0.7.0rc0"
+    ],
+    [
+        "dicttoxml",
+        "dicttoxml",
+        "1.7.4"
+    ],
+    [
+        "promise",
+        "promise",
+        "2.3"
+    ],
+    [
+        "vertica-python",
+        "vertica-python",
+        "1.0.1"
+    ],
+    [
+        "genson",
+        "genson",
+        "1.2.2"
+    ],
+    [
+        "h11",
+        "h11",
+        "0.12.0"
+    ],
+    [
+        "argh",
+        "argh",
+        "0.26.2"
+    ],
+    [
+        "traits",
+        "traits",
+        "6.2.0"
+    ],
+    [
+        "multiprocess",
+        "multiprocess",
+        "0.70.12.2"
+    ],
+    [
+        "ecos",
+        "ecos",
+        "2.0.7.post1"
+    ],
+    [
+        "fsspec",
+        "fsspec",
+        "2021.10.0"
+    ],
+    [
+        "yt",
+        "yt",
+        "4.0.1"
+    ],
+    [
+        "pg8000",
+        "pg8000",
+        "1.21.2"
+    ],
+    [
+        "nest-asyncio",
+        "nest-asyncio",
+        "1.5.1"
+    ],
+    [
+        "argon2-cffi",
+        "argon2-cffi",
+        "21.1.0"
+    ],
+    [
+        "argon2_cffi",
+        "argon2_cffi",
+        "21.1.0"
+    ],
+    [
+        "terminado",
+        "terminado",
+        "0.12.1"
+    ],
+    [
+        "semver",
+        "semver",
+        "3.0.0.dev2"
+    ],
+    [
+        "asynctest",
+        "asynctest",
+        "0.13.0"
+    ],
+    [
+        "texttable",
+        "texttable",
+        "1.6.4"
+    ],
+    [
+        "flask-bcrypt",
+        "flask-bcrypt",
+        "0.7.1"
+    ],
+    [
+        "flaky",
+        "flaky",
+        "3.7.0"
+    ],
+    [
+        "re2",
+        "re2",
+        "0.2.24"
+    ],
+    [
+        "conda-pack",
+        "conda-pack",
+        "0.6.0"
+    ],
+    [
+        "elasticsearch-async",
+        "elasticsearch-async",
+        "6.2.0"
+    ],
+    [
+        "ijson",
+        "ijson",
+        "3.1.4"
+    ],
+    [
+        "ansi2html",
+        "ansi2html",
+        "1.6.0"
+    ],
+    [
+        "blosc",
+        "blosc",
+        "1.10.4"
+    ],
+    [
+        "saspy",
+        "saspy",
+        "3.7.5"
+    ],
+    [
+        "tornado-json",
+        "tornado-json",
+        "1.3.4"
+    ],
+    [
+        "zope",
+        "zope",
+        "5.3"
+    ],
+    [
+        "requests_ntlm",
+        "requests_ntlm",
+        "1.1.0"
+    ],
+    [
+        "jaydebeapi",
+        "jaydebeapi",
+        "1.2.3"
+    ],
+    [
+        "netifaces",
+        "netifaces",
+        "0.11.0"
+    ],
+    [
+        "scikit-build",
+        "scikit-build",
+        "0.12.0"
+    ],
+    [
+        "atomicwrites",
+        "atomicwrites",
+        "1.4.0"
+    ],
+    [
+        "aws-xray-sdk",
+        "aws-xray-sdk",
+        "2.8.0"
+    ],
+    [
+        "wheel",
+        "wheel",
+        "0.37.0"
+    ],
+    [
+        "chaco",
+        "chaco",
+        "5.0.0"
+    ],
+    [
+        "metakernel",
+        "metakernel",
+        "0.27.5"
+    ],
+    [
+        "conda-build",
+        "conda-build",
+        "2.1.5"
+    ],
+    [
+        "bandersnatch",
+        "bandersnatch",
+        "5.0.0"
+    ],
+    [
+        "amqp",
+        "amqp",
+        "5.0.6"
+    ],
+    [
+        "zstandard",
+        "zstandard",
+        "0.15.2"
+    ],
+    [
+        "configobj",
+        "configobj",
+        "5.0.6"
+    ],
+    [
+        "pytest-selenium",
+        "pytest-selenium",
+        "2.0.1"
+    ],
+    [
+        "multidict",
+        "multidict",
+        "5.2.0"
+    ],
+    [
+        "google-pasta",
+        "google-pasta",
+        "0.2.0"
+    ],
+    [
+        "traitsui",
+        "traitsui",
+        "7.2.1"
+    ],
+    [
+        "zope.interface",
+        "zope.interface",
+        "5.4.0"
+    ],
+    [
+        "wcwidth",
+        "wcwidth",
+        "0.2.5"
+    ],
+    [
+        "xxhash",
+        "xxhash",
+        "2.0.2"
+    ],
+    [
+        "flake8-import-order",
+        "flake8-import-order",
+        "0.18.1"
+    ],
+    [
+        "jupyter_client",
+        "jupyter_client",
+        "7.0.5"
+    ],
+    [
+        "cssselect",
+        "cssselect",
+        "1.1.0"
+    ],
+    [
+        "dask-searchcv",
+        "dask-searchcv",
+        "0.2.0"
+    ],
+    [
+        "plac",
+        "plac",
+        "1.3.3"
+    ],
+    [
+        "param",
+        "param",
+        "1.11.22a4"
+    ],
+    [
+        "defusedxml",
+        "defusedxml",
+        "0.7.1"
+    ],
+    [
+        "hyperlink",
+        "hyperlink",
+        "21.0.0"
+    ],
+    [
+        "tensorflow-estimator",
+        "tensorflow-estimator",
+        "2.6.0"
+    ],
+    [
+        "conllu",
+        "conllu",
+        "4.4.1"
+    ],
+    [
+        "scikit-learn-intelex",
+        "scikit-learn-intelex",
+        "2021.4.0"
+    ],
+    [
+        "daal4py",
+        "daal4py",
+        "2021.4.0"
+    ],
+    [
+        "requests-kerberos",
+        "requests-kerberos",
+        "0.12.0"
+    ],
+    [
+        "asn1crypto",
+        "asn1crypto",
+        "1.4.0"
+    ],
+    [
+        "srsly",
+        "srsly",
+        "2.4.1"
+    ],
+    [
+        "huggingface_hub",
+        "huggingface_hub",
+        "0.0.18"
+    ],
+    [
+        "freetype-py",
+        "freetype-py",
+        "2.2.0"
+    ],
+    [
+        "adal",
+        "adal",
+        "1.2.7"
+    ],
+    [
+        "commonmark",
+        "commonmark",
+        "0.9.1"
+    ],
+    [
+        "croniter",
+        "croniter",
+        "1.0.15"
+    ],
+    [
+        "colour",
+        "colour",
+        "0.1.5"
+    ],
+    [
+        "typing_inspect",
+        "typing_inspect",
+        "0.7.1"
+    ],
+    [
+        "pytest-rerunfailures",
+        "pytest-rerunfailures",
+        "10.2"
+    ],
+    [
+        "futures",
+        "futures",
+        "3.3.0"
+    ],
+    [
+        "queuelib",
+        "queuelib",
+        "1.6.2"
+    ],
+    [
+        "bytecode",
+        "bytecode",
+        "0.13.0"
+    ],
+    [
+        "semantic_version",
+        "semantic_version",
+        "2.8.5"
+    ],
+    [
+        "flask-openid",
+        "flask-openid",
+        "1.3.0"
+    ],
+    [
+        "pytimeparse",
+        "pytimeparse",
+        "1.1.8"
+    ],
+    [
+        "httpcore",
+        "httpcore",
+        "0.13.7"
+    ],
+    [
+        "fast-histogram",
+        "fast-histogram",
+        "0.10"
+    ],
+    [
+        "pytest-ordering",
+        "pytest-ordering",
+        "0.6"
+    ],
+    [
+        "testfixtures",
+        "testfixtures",
+        "6.18.3"
+    ],
+    [
+        "aiohttp-jinja2",
+        "aiohttp-jinja2",
+        "1.5"
+    ],
+    [
+        "pretend",
+        "pretend",
+        "1.0.9"
+    ],
+    [
+        "clickhouse-sqlalchemy",
+        "clickhouse-sqlalchemy",
+        "0.1.6"
+    ],
+    [
+        "pyobjc-core",
+        "pyobjc-core",
+        "8.0b1"
+    ],
+    [
+        "pyobjc-framework-fsevents",
+        "pyobjc-framework-fsevents",
+        "8.0b1"
+    ],
+    [
+        "pyobjc-framework-cocoa",
+        "pyobjc-framework-cocoa",
+        "8.0b1"
+    ],
+    [
+        "pims",
+        "pims",
+        "0.5"
+    ],
+    [
+        "toro",
+        "toro",
+        "1.0.1"
+    ],
+    [
+        "curtsies",
+        "curtsies",
+        "0.3.7"
+    ],
+    [
+        "py-cpuinfo",
+        "py-cpuinfo",
+        "8.0.0"
+    ],
+    [
+        "hidapi",
+        "hidapi",
+        "0.10.1"
+    ],
+    [
+        "comtypes",
+        "comtypes",
+        "1.1.10"
+    ],
+    [
+        "trollius",
+        "trollius",
+        "2.2.1"
+    ],
+    [
+        "pyasn1",
+        "pyasn1",
+        "0.4.8"
+    ],
+    [
+        "lazy-object-proxy",
+        "lazy-object-proxy",
+        "1.6.0"
+    ],
+    [
+        "iminuit",
+        "iminuit",
+        "2.8.3"
+    ],
+    [
+        "typed-ast",
+        "typed-ast",
+        "1.4.3"
+    ],
+    [
+        "nbserverproxy",
+        "nbserverproxy",
+        "0.8.8"
+    ],
+    [
+        "distro",
+        "distro",
+        "1.6.0"
+    ],
+    [
+        "visvis",
+        "visvis",
+        "1.13.0"
+    ],
+    [
+        "tifffile",
+        "tifffile",
+        "2021.8.30"
+    ],
+    [
+        "openapi-spec-validator",
+        "openapi-spec-validator",
+        "0.3.1"
+    ],
+    [
+        "python-gflags",
+        "python-gflags",
+        "3.1.2"
+    ],
+    [
+        "ipaddr",
+        "ipaddr",
+        "2.2.0"
+    ],
+    [
+        "send2trash",
+        "send2trash",
+        "1.8.1b0"
+    ],
+    [
+        "mgwr",
+        "mgwr",
+        "2.1.2"
+    ],
+    [
+        "aiohttp-cors",
+        "aiohttp-cors",
+        "0.7.0"
+    ],
+    [
+        "sas_kernel",
+        "sas_kernel",
+        "2.4.11"
+    ],
+    [
+        "threadpoolctl",
+        "threadpoolctl",
+        "3.0.0"
+    ],
+    [
+        "charset-normalizer",
+        "charset-normalizer",
+        "2.0.6"
+    ],
+    [
+        "hupper",
+        "hupper",
+        "1.10.3"
+    ],
+    [
+        "svn",
+        "svn",
+        "1.0.1"
+    ],
+    [
+        "lz4",
+        "lz4",
+        "3.1.3"
+    ],
+    [
+        "python-engineio",
+        "python-engineio",
+        "4.2.1"
+    ],
+    [
+        "jq",
+        "jq",
+        "1.2.1"
+    ],
+    [
+        "func_timeout",
+        "func_timeout",
+        "4.3.5"
+    ],
+    [
+        "codecov",
+        "codecov",
+        "2.1.12"
+    ],
+    [
+        "astunparse",
+        "astunparse",
+        "1.6.3"
+    ],
+    [
+        "click-repl",
+        "click-repl",
+        "0.2.0"
+    ],
+    [
+        "fasteners",
+        "fasteners",
+        "0.16.3"
+    ],
+    [
+        "py7zr",
+        "py7zr",
+        "0.16.1"
+    ],
+    [
+        "cloudant",
+        "cloudant",
+        "2.15.0"
+    ],
+    [
+        "subprocess32",
+        "subprocess32",
+        "3.5.4"
+    ],
+    [
+        "jupyterhub-ldapauthenticator",
+        "jupyterhub-ldapauthenticator",
+        "1.3.2"
+    ],
+    [
+        "idna",
+        "idna",
+        "3.2"
+    ],
+    [
+        "opentracing_instrumentation",
+        "opentracing_instrumentation",
+        "3.3.1"
+    ],
+    [
+        "portalocker",
+        "portalocker",
+        "2.3.2"
+    ],
+    [
+        "msgpack-numpy",
+        "msgpack-numpy",
+        "0.4.7.1"
+    ],
+    [
+        "jsonlines",
+        "jsonlines",
+        "2.0.0"
+    ],
+    [
+        "nbpresent",
+        "nbpresent",
+        "3.0.0"
+    ],
+    [
+        "pycosat",
+        "pycosat",
+        "0.6.3"
+    ],
+    [
+        "mako",
+        "mako",
+        "1.1.5"
+    ],
+    [
+        "olefile",
+        "olefile",
+        "0.47.dev4"
+    ],
+    [
+        "pyclipper",
+        "pyclipper",
+        "1.3.0"
+    ],
+    [
+        "dash-html-components",
+        "dash-html-components",
+        "2.0.0"
+    ],
+    [
+        "deprecated",
+        "deprecated",
+        "1.2.13"
+    ],
+    [
+        "poetry-core",
+        "poetry-core",
+        "1.1.0a6"
+    ],
+    [
+        "flex",
+        "flex",
+        "6.14.1"
+    ],
+    [
+        "workerpool",
+        "workerpool",
+        "0.9.4"
+    ],
+    [
+        "ptyprocess",
+        "ptyprocess",
+        "0.7.0"
+    ],
+    [
+        "fastcache",
+        "fastcache",
+        "1.1.0"
+    ],
+    [
+        "spaghetti",
+        "spaghetti",
+        "1.6.4"
+    ],
+    [
+        "lerc",
+        "lerc",
+        "0.1.0"
+    ],
+    [
+        "tzlocal",
+        "tzlocal",
+        "4.0b2"
+    ],
+    [
+        "pooch",
+        "pooch",
+        "1.5.1"
+    ],
+    [
+        "dask-image",
+        "dask-image",
+        "0.6.0"
+    ],
+    [
+        "chameleon",
+        "chameleon",
+        "3.9.1"
+    ],
+    [
+        "pyprof2calltree",
+        "pyprof2calltree",
+        "1.4.5"
+    ],
+    [
+        "gitdb2",
+        "gitdb2",
+        "4.0.2"
+    ],
+    [
+        "gitdb",
+        "gitdb",
+        "4.0.7"
+    ],
+    [
+        "cheroot",
+        "cheroot",
+        "8.5.2"
+    ],
+    [
+        "aioitertools",
+        "aioitertools",
+        "0.8.0"
+    ],
+    [
+        "hdfs3",
+        "hdfs3",
+        "0.3.1"
+    ],
+    [
+        "poyo",
+        "poyo",
+        "0.5.0"
+    ],
+    [
+        "s3transfer",
+        "s3transfer",
+        "0.5.0"
+    ],
+    [
+        "pycares",
+        "pycares",
+        "4.0.0"
+    ],
+    [
+        "pytoml",
+        "pytoml",
+        "0.1.21"
+    ],
+    [
+        "readme_renderer",
+        "readme_renderer",
+        "30.0"
+    ],
+    [
+        "esda",
+        "esda",
+        "2.4.1"
+    ],
+    [
+        "google-cloud-storage",
+        "google-cloud-storage",
+        "1.42.3"
+    ],
+    [
+        "python-oauth2",
+        "python-oauth2",
+        "1.1.1"
+    ],
+    [
+        "brotlicffi",
+        "brotlicffi",
+        "1.0.9.2"
+    ],
+    [
+        "brotlipy",
+        "brotlipy",
+        "0.7.0"
+    ],
+    [
+        "pykdtree",
+        "pykdtree",
+        "1.3.4"
+    ],
+    [
+        "tblib",
+        "tblib",
+        "1.7.0"
+    ],
+    [
+        "pystan",
+        "pystan",
+        "3.2.0"
+    ],
+    [
+        "portpicker",
+        "portpicker",
+        "1.5.0b1"
+    ],
+    [
+        "glob2",
+        "glob2",
+        "0.7"
+    ],
+    [
+        "gast",
+        "gast",
+        "0.5.2"
+    ],
+    [
+        "tiledb",
+        "tiledb",
+        "0.10.2"
+    ],
+    [
+        "vega_datasets",
+        "vega_datasets",
+        "0.9.0"
+    ],
+    [
+        "affine",
+        "affine",
+        "2.3.0"
+    ],
+    [
+        "multimethod",
+        "multimethod",
+        "1.6"
+    ],
+    [
+        "wurlitzer",
+        "wurlitzer",
+        "3.0.2"
+    ],
+    [
+        "binaryornot",
+        "binaryornot",
+        "0.4.4"
+    ],
+    [
+        "pytest-tornado",
+        "pytest-tornado",
+        "0.8.1"
+    ],
+    [
+        "pyaml",
+        "pyaml",
+        "21.8.3"
+    ],
+    [
+        "click-plugins",
+        "click-plugins",
+        "1.1.1"
+    ],
+    [
+        "python_http_client",
+        "python_http_client",
+        "3.3.3"
+    ],
+    [
+        "imagesize",
+        "imagesize",
+        "1.2.0"
+    ],
+    [
+        "soupsieve",
+        "soupsieve",
+        "2.2.1"
+    ],
+    [
+        "teradata",
+        "teradata",
+        "15.10.0.21"
+    ],
+    [
+        "htmlmin",
+        "htmlmin",
+        "0.1.12"
+    ],
+    [
+        "cctools",
+        "cctools",
+        "7.0.17"
+    ],
+    [
+        "pyjks",
+        "pyjks",
+        "20.0.0"
+    ],
+    [
+        "incremental",
+        "incremental",
+        "21.3.0"
+    ],
+    [
+        "binstar",
+        "binstar",
+        "0.11.0"
+    ],
+    [
+        "drmaa",
+        "drmaa",
+        "0.7.9"
+    ],
+    [
+        "win_unicode_console",
+        "win_unicode_console",
+        "0.5"
+    ],
+    [
+        "isodate",
+        "isodate",
+        "0.6.0"
+    ],
+    [
+        "pathy",
+        "pathy",
+        "0.6.0"
+    ],
+    [
+        "pysmi",
+        "pysmi",
+        "0.3.4"
+    ],
+    [
+        "catalogue",
+        "catalogue",
+        "2.0.6"
+    ],
+    [
+        "jsonpointer",
+        "jsonpointer",
+        "2.1"
+    ],
+    [
+        "pytest-flake8",
+        "pytest-flake8",
+        "1.0.7"
+    ],
+    [
+        "zstd",
+        "zstd",
+        "1.5.0.2"
+    ],
+    [
+        "ipaddress",
+        "ipaddress",
+        "1.0.23"
+    ],
+    [
+        "conda-env",
+        "conda-env",
+        "2.4.2"
+    ],
+    [
+        "shellingham",
+        "shellingham",
+        "1.4.0"
+    ],
+    [
+        "pytest-subtests",
+        "pytest-subtests",
+        "0.5.0"
+    ],
+    [
+        "libarchive",
+        "libarchive",
+        "0.4.7"
+    ],
+    [
+        "junit-xml",
+        "junit-xml",
+        "1.9"
+    ],
+    [
+        "intervals",
+        "intervals",
+        "0.9.2"
+    ],
+    [
+        "google-auth-oauthlib",
+        "google-auth-oauthlib",
+        "0.4.6"
+    ],
+    [
+        "json5",
+        "json5",
+        "0.9.6"
+    ],
+    [
+        "libtiff",
+        "libtiff",
+        "0.4.2"
+    ],
+    [
+        "testtools",
+        "testtools",
+        "2.5.0"
+    ],
+    [
+        "vine",
+        "vine",
+        "5.0.0"
+    ],
+    [
+        "mypy_extensions",
+        "mypy_extensions",
+        "0.4.3"
+    ],
+    [
+        "partd",
+        "partd",
+        "1.2.0"
+    ],
+    [
+        "pathspec",
+        "pathspec",
+        "0.9.0"
+    ],
+    [
+        "async_generator",
+        "async_generator",
+        "1.10"
+    ],
+    [
+        "pytest-timeout",
+        "pytest-timeout",
+        "1.4.2"
+    ],
+    [
+        "bsdiff4",
+        "bsdiff4",
+        "1.2.1"
+    ],
+    [
+        "pyface",
+        "pyface",
+        "7.3.0"
+    ],
+    [
+        "mapclassify",
+        "mapclassify",
+        "2.4.3"
+    ],
+    [
+        "patch",
+        "patch",
+        "1.16"
+    ],
+    [
+        "mockito",
+        "mockito",
+        "1.2.2"
+    ],
+    [
+        "jupyter_dashboards_bundlers",
+        "jupyter_dashboards_bundlers",
+        "0.9.1"
+    ],
+    [
+        "splot",
+        "splot",
+        "1.1.4"
+    ],
+    [
+        "asciitree",
+        "asciitree",
+        "0.3.3"
+    ],
+    [
+        "nbclient",
+        "nbclient",
+        "0.5.4"
+    ],
+    [
+        "dask-glm",
+        "dask-glm",
+        "0.2.0"
+    ],
+    [
+        "visions",
+        "visions",
+        "0.7.4"
+    ],
+    [
+        "mdp",
+        "mdp",
+        "3.6"
+    ],
+    [
+        "pyls-black",
+        "pyls-black",
+        "0.4.7"
+    ],
+    [
+        "gridmap",
+        "gridmap",
+        "0.15.0"
+    ],
+    [
+        "service_identity",
+        "service_identity",
+        "21.1.0"
+    ],
+    [
+        "sshpubkeys",
+        "sshpubkeys",
+        "3.3.1"
+    ],
+    [
+        "xmlsec",
+        "xmlsec",
+        "1.3.12"
+    ],
+    [
+        "pyramid_jinja2",
+        "pyramid_jinja2",
+        "2.8"
+    ],
+    [
+        "contextvars",
+        "contextvars",
+        "2.4"
+    ],
+    [
+        "tobler",
+        "tobler",
+        "0.8.2"
+    ],
+    [
+        "pytest-azurepipelines",
+        "pytest-azurepipelines",
+        "1.0.0rc5"
+    ],
+    [
+        "python-editor",
+        "python-editor",
+        "1.0.4"
+    ],
+    [
+        "geotiff",
+        "geotiff",
+        "0.2.3"
+    ],
+    [
+        "cmake",
+        "cmake",
+        "3.21.3"
+    ],
+    [
+        "requests-ftp",
+        "requests-ftp",
+        "0.3.1"
+    ],
+    [
+        "pylev",
+        "pylev",
+        "1.4.0"
+    ],
+    [
+        "tensorflow-metadata",
+        "tensorflow-metadata",
+        "1.2.0"
+    ],
+    [
+        "chromedriver-binary",
+        "chromedriver-binary",
+        "95.0.4638.17.0"
+    ],
+    [
+        "entrypoints",
+        "entrypoints",
+        "0.3"
+    ],
+    [
+        "python-jsonrpc-server",
+        "python-jsonrpc-server",
+        "0.4.0"
+    ],
+    [
+        "deprecation",
+        "deprecation",
+        "2.1.0"
+    ],
+    [
+        "clikit",
+        "clikit",
+        "0.6.2"
+    ],
+    [
+        "cppy",
+        "cppy",
+        "1.1.0"
+    ],
+    [
+        "pathlib2",
+        "pathlib2",
+        "2.3.6"
+    ],
+    [
+        "bash",
+        "bash",
+        "0.6"
+    ],
+    [
+        "preshed",
+        "preshed",
+        "3.0.5"
+    ],
+    [
+        "monotonic",
+        "monotonic",
+        "1.6"
+    ],
+    [
+        "numcodecs",
+        "numcodecs",
+        "0.9.1"
+    ],
+    [
+        "flask-compress",
+        "flask-compress",
+        "1.10.1"
+    ],
+    [
+        "requests-file",
+        "requests-file",
+        "1.5.1"
+    ],
+    [
+        "jinja2-time",
+        "jinja2-time",
+        "0.2.0"
+    ],
+    [
+        "macfsevents",
+        "macfsevents",
+        "0.8.1"
+    ],
+    [
+        "btrees",
+        "btrees",
+        "4.9.2"
+    ],
+    [
+        "whichcraft",
+        "whichcraft",
+        "0.6.1"
+    ],
+    [
+        "chest",
+        "chest",
+        "0.2.3"
+    ],
+    [
+        "envisage",
+        "envisage",
+        "6.0.1"
+    ],
+    [
+        "pytest-variables",
+        "pytest-variables",
+        "1.9.0"
+    ],
+    [
+        "orderedmultidict",
+        "orderedmultidict",
+        "1.0.1"
+    ],
+    [
+        "hdmedians",
+        "hdmedians",
+        "0.14.2"
+    ],
+    [
+        "nosexcover",
+        "nosexcover",
+        "1.0.11"
+    ],
+    [
+        "backports.lzma",
+        "backports.lzma",
+        "0.0.14"
+    ],
+    [
+        "cgroupspy",
+        "cgroupspy",
+        "0.2.1"
+    ],
+    [
+        "pytest-runner",
+        "pytest-runner",
+        "5.3.1"
+    ],
+    [
+        "sniffio",
+        "sniffio",
+        "1.2.0"
+    ],
+    [
+        "secretstorage",
+        "secretstorage",
+        "3.3.1"
+    ],
+    [
+        "pickleshare",
+        "pickleshare",
+        "0.7.5"
+    ],
+    [
+        "text-unidecode",
+        "text-unidecode",
+        "1.3"
+    ],
+    [
+        "hpack",
+        "hpack",
+        "4.0.0"
+    ],
+    [
+        "knit",
+        "knit",
+        "0.2.4"
+    ],
+    [
+        "transaction",
+        "transaction",
+        "3.0.1"
+    ],
+    [
+        "aenum",
+        "aenum",
+        "3.1.0"
+    ],
+    [
+        "javaobj-py3",
+        "javaobj-py3",
+        "0.4.3"
+    ],
+    [
+        "cycler",
+        "cycler",
+        "0.10.0"
+    ],
+    [
+        "giddy",
+        "giddy",
+        "2.3.3"
+    ],
+    [
+        "abstract-rendering",
+        "abstract-rendering",
+        "0.5.1"
+    ],
+    [
+        "pkgconfig",
+        "pkgconfig",
+        "1.5.5"
+    ],
+    [
+        "functools32",
+        "functools32",
+        "3.2.3.post2"
+    ],
+    [
+        "smmap2",
+        "smmap2",
+        "3.0.1"
+    ],
+    [
+        "pandoc",
+        "pandoc",
+        "2.0a4"
+    ],
+    [
+        "smmap",
+        "smmap",
+        "4.0.0"
+    ],
+    [
+        "python-utils",
+        "python-utils",
+        "2.5.6"
+    ],
+    [
+        "archspec",
+        "archspec",
+        "0.1.2"
+    ],
+    [
+        "qds-sdk",
+        "qds-sdk",
+        "1.16.1"
+    ],
+    [
+        "ffmpeg",
+        "ffmpeg",
+        "1.4"
+    ],
+    [
+        "pathtools",
+        "pathtools",
+        "0.1.2"
+    ],
+    [
+        "appnope",
+        "appnope",
+        "0.1.2"
+    ],
+    [
+        "pywinpty",
+        "pywinpty",
+        "1.1.4"
+    ],
+    [
+        "essential_generators",
+        "essential_generators",
+        "1.0"
+    ],
+    [
+        "macholib",
+        "macholib",
+        "1.15.2"
+    ],
+    [
+        "mkl_fft",
+        "mkl_fft",
+        "1.3.0"
+    ],
+    [
+        "ntlm-auth",
+        "ntlm-auth",
+        "1.5.0"
+    ],
+    [
+        "pyinstaller-hooks-contrib",
+        "pyinstaller-hooks-contrib",
+        "2021.3"
+    ],
+    [
+        "pastel",
+        "pastel",
+        "0.2.1"
+    ],
+    [
+        "glog",
+        "glog",
+        "0.3.1"
+    ],
+    [
+        "argparse",
+        "argparse",
+        "1.4.0"
+    ],
+    [
+        "winkerberos",
+        "winkerberos",
+        "0.8.0"
+    ],
+    [
+        "chai",
+        "chai",
+        "1.1.2"
+    ],
+    [
+        "pytest-flakes",
+        "pytest-flakes",
+        "4.0.3"
+    ],
+    [
+        "beniget",
+        "beniget",
+        "0.4.1"
+    ],
+    [
+        "qstylizer",
+        "qstylizer",
+        "0.2.1"
+    ],
+    [
+        "cmarkgfm",
+        "cmarkgfm",
+        "0.6.0"
+    ],
+    [
+        "picklable-itertools",
+        "picklable-itertools",
+        "0.1.1"
+    ],
+    [
+        "persistent",
+        "persistent",
+        "4.7.0"
+    ],
+    [
+        "flask-json",
+        "flask-json",
+        "0.3.4"
+    ],
+    [
+        "intake-xarray",
+        "intake-xarray",
+        "0.5.0"
+    ],
+    [
+        "pytest-forked",
+        "pytest-forked",
+        "1.3.0"
+    ],
+    [
+        "pytest-metadata",
+        "pytest-metadata",
+        "1.11.0"
+    ],
+    [
+        "spint",
+        "spint",
+        "1.0.7"
+    ],
+    [
+        "pointpats",
+        "pointpats",
+        "2.2.0"
+    ],
+    [
+        "python3-openid",
+        "python3-openid",
+        "3.2.0"
+    ],
+    [
+        "zope.event",
+        "zope.event",
+        "4.5.0"
+    ],
+    [
+        "pytest-replay",
+        "pytest-replay",
+        "1.4.0"
+    ],
+    [
+        "cvxcanon",
+        "cvxcanon",
+        "0.1.2"
+    ],
+    [
+        "backports.functools_lru_cache",
+        "backports.functools_lru_cache",
+        "1.6.4"
+    ],
+    [
+        "google-api-core",
+        "google-api-core",
+        "2.0.1"
+    ],
+    [
+        "geos",
+        "geos",
+        "0.2.3"
+    ],
+    [
+        "google-resumable-media",
+        "google-resumable-media",
+        "2.0.3"
+    ],
+    [
+        "cligj",
+        "cligj",
+        "0.7.2"
+    ],
+    [
+        "hyperframe",
+        "hyperframe",
+        "6.0.1"
+    ],
+    [
+        "pure-sasl",
+        "pure-sasl",
+        "0.6.2"
+    ],
+    [
+        "pytest-doctestplus",
+        "pytest-doctestplus",
+        "0.11.0"
+    ],
+    [
+        "libpcap",
+        "libpcap",
+        "1.10.0b15"
+    ],
+    [
+        "pyasn1-modules",
+        "pyasn1-modules",
+        "0.2.8"
+    ],
+    [
+        "pamela",
+        "pamela",
+        "1.0.0"
+    ],
+    [
+        "zope.sqlalchemy",
+        "zope.sqlalchemy",
+        "1.6"
+    ],
+    [
+        "setuptools-git",
+        "setuptools-git",
+        "1.2"
+    ],
+    [
+        "epoxy",
+        "epoxy",
+        "1.0"
+    ],
+    [
+        "importlib-metadata",
+        "importlib-metadata",
+        "4.8.1"
+    ],
+    [
+        "importlib_metadata",
+        "importlib_metadata",
+        "4.8.1"
+    ],
+    [
+        "multi_key_dict",
+        "multi_key_dict",
+        "2.0.3"
+    ],
+    [
+        "cachy",
+        "cachy",
+        "0.3.0"
+    ],
+    [
+        "spreg",
+        "spreg",
+        "1.2.4"
+    ],
+    [
+        "extension-helpers",
+        "extension-helpers",
+        "0.1"
+    ],
+    [
+        "tranquilizer",
+        "tranquilizer",
+        "0.6.0"
+    ],
+    [
+        "jdcal",
+        "jdcal",
+        "1.4.1"
+    ],
+    [
+        "cookies",
+        "cookies",
+        "2.2.1"
+    ],
+    [
+        "vcversioner",
+        "vcversioner",
+        "2.16.0.0"
+    ],
+    [
+        "itemadapter",
+        "itemadapter",
+        "0.4.0"
+    ],
+    [
+        "pydata-google-auth",
+        "pydata-google-auth",
+        "1.2.0"
+    ],
+    [
+        "configparser",
+        "configparser",
+        "5.0.2"
+    ],
+    [
+        "unicodedata2",
+        "unicodedata2",
+        "13.0.0.post2"
+    ],
+    [
+        "zope.component",
+        "zope.component",
+        "5.0.1"
+    ],
+    [
+        "tzdata",
+        "tzdata",
+        "2021.2.post0"
+    ],
+    [
+        "priority",
+        "priority",
+        "2.0.0"
+    ],
+    [
+        "murmurhash",
+        "murmurhash",
+        "1.1.0.dev0"
+    ],
+    [
+        "webencodings",
+        "webencodings",
+        "0.5.1"
+    ],
+    [
+        "itemloaders",
+        "itemloaders",
+        "1.0.4"
+    ],
+    [
+        "pyutilib",
+        "pyutilib",
+        "6.0.0"
+    ],
+    [
+        "mkl-service",
+        "mkl-service",
+        "2.4.0"
+    ],
+    [
+        "git-lfs",
+        "git-lfs",
+        "1.6"
+    ],
+    [
+        "translationstring",
+        "translationstring",
+        "1.4"
+    ],
+    [
+        "iniconfig",
+        "iniconfig",
+        "1.1.1"
+    ],
+    [
+        "dbf",
+        "dbf",
+        "0.99.1"
+    ],
+    [
+        "outcome",
+        "outcome",
+        "1.1.0"
+    ],
+    [
+        "blz",
+        "blz",
+        "0.6.2"
+    ],
+    [
+        "spyder-kernels",
+        "spyder-kernels",
+        "2.1.3"
+    ],
+    [
+        "menuinst",
+        "menuinst",
+        "1.0.0"
+    ],
+    [
+        "pickle5",
+        "pickle5",
+        "0.0.11"
+    ],
+    [
+        "apipkg",
+        "apipkg",
+        "2.0.0"
+    ],
+    [
+        "pyramid_mako",
+        "pyramid_mako",
+        "1.1.0"
+    ],
+    [
+        "zipp",
+        "zipp",
+        "3.6.0"
+    ],
+    [
+        "testpath",
+        "testpath",
+        "0.5.0"
+    ],
+    [
+        "bkcharts",
+        "bkcharts",
+        "0.2"
+    ],
+    [
+        "win_inet_pton",
+        "win_inet_pton",
+        "1.1.0"
+    ],
+    [
+        "diff-match-patch",
+        "diff-match-patch",
+        "20200713"
+    ],
+    [
+        "imagecodecs",
+        "imagecodecs",
+        "2021.8.26"
+    ],
+    [
+        "glue-vispy-viewers",
+        "glue-vispy-viewers",
+        "1.0.3"
+    ],
+    [
+        "pyepsg",
+        "pyepsg",
+        "0.4.0"
+    ],
+    [
+        "nose-exclude",
+        "nose-exclude",
+        "0.5.0"
+    ],
+    [
+        "nodejs",
+        "nodejs",
+        "0.1.1"
+    ],
+    [
+        "polly",
+        "polly",
+        "0.6.4"
+    ],
+    [
+        "meld3",
+        "meld3",
+        "2.0.1"
+    ],
+    [
+        "d2to1",
+        "d2to1",
+        "0.2.12.post1"
+    ],
+    [
+        "constantly",
+        "constantly",
+        "15.1.0"
+    ],
+    [
+        "pytest-base-url",
+        "pytest-base-url",
+        "1.4.2"
+    ],
+    [
+        "sqlalchemy-jsonfield",
+        "sqlalchemy-jsonfield",
+        "1.0.0"
+    ],
+    [
+        "pyzstd",
+        "pyzstd",
+        "0.15.0"
+    ],
+    [
+        "thrift_sasl",
+        "thrift_sasl",
+        "0.4.3"
+    ],
+    [
+        "orange-canvas-core",
+        "orange-canvas-core",
+        "0.1.22"
+    ],
+    [
+        "ndg-httpsclient",
+        "ndg-httpsclient",
+        "0.5.1"
+    ],
+    [
+        "editables",
+        "editables",
+        "0.2"
+    ],
+    [
+        "setuptools_scm_git_archive",
+        "setuptools_scm_git_archive",
+        "1.1"
+    ],
+    [
+        "importlib_resources",
+        "importlib_resources",
+        "5.2.2"
+    ],
+    [
+        "twofish",
+        "twofish",
+        "0.3.0"
+    ],
+    [
+        "infinity",
+        "infinity",
+        "1.5"
+    ],
+    [
+        "setuptools-scm-git-archive",
+        "setuptools-scm-git-archive",
+        "1.1"
+    ],
+    [
+        "pytzdata",
+        "pytzdata",
+        "2020.1"
+    ],
+    [
+        "json-merge-patch",
+        "json-merge-patch",
+        "0.2"
+    ],
+    [
+        "keyrings.alt",
+        "keyrings.alt",
+        "4.1.0"
+    ],
+    [
+        "pyerfa",
+        "pyerfa",
+        "2.0.0"
+    ],
+    [
+        "mpg123",
+        "mpg123",
+        "0.4"
+    ],
+    [
+        "zc.lockfile",
+        "zc.lockfile",
+        "2.0"
+    ],
+    [
+        "snuggs",
+        "snuggs",
+        "1.4.7"
+    ],
+    [
+        "echo",
+        "echo",
+        "0.5"
+    ],
+    [
+        "pastedeploy",
+        "pastedeploy",
+        "2.1.1"
+    ],
+    [
+        "bz2file",
+        "bz2file",
+        "0.98"
+    ],
+    [
+        "pytest-csv",
+        "pytest-csv",
+        "3.0.0"
+    ],
+    [
+        "ruby",
+        "ruby",
+        "0.0.2"
+    ],
+    [
+        "libclang",
+        "libclang",
+        "12.0.0"
+    ],
+    [
+        "iso8601",
+        "iso8601",
+        "1.0.0a1"
+    ],
+    [
+        "fastrlock",
+        "fastrlock",
+        "0.6"
+    ],
+    [
+        "backcall",
+        "backcall",
+        "0.2.0"
+    ],
+    [
+        "fmt",
+        "fmt",
+        "0.3.1"
+    ],
+    [
+        "prison",
+        "prison",
+        "0.2.1"
+    ],
+    [
+        "spglm",
+        "spglm",
+        "1.0.8"
+    ],
+    [
+        "crashtest",
+        "crashtest",
+        "0.3.1"
+    ],
+    [
+        "swagger-ui-bundle",
+        "swagger-ui-bundle",
+        "0.0.9"
+    ],
+    [
+        "jsondate",
+        "jsondate",
+        "0.1.2"
+    ],
+    [
+        "slicerator",
+        "slicerator",
+        "1.0.0"
+    ],
+    [
+        "enscons",
+        "enscons",
+        "0.28.0"
+    ],
+    [
+        "mkl_random",
+        "mkl_random",
+        "1.2.2"
+    ],
+    [
+        "bison",
+        "bison",
+        "0.1.3"
+    ],
+    [
+        "pytest-arraydiff",
+        "pytest-arraydiff",
+        "0.3"
+    ],
+    [
+        "nbsmoke",
+        "nbsmoke",
+        "0.5.0"
+    ],
+    [
+        "rbc",
+        "rbc",
+        "0.2.0"
+    ],
+    [
+        "google-cloud-core",
+        "google-cloud-core",
+        "2.0.0"
+    ],
+    [
+        "maven",
+        "maven",
+        "0.1.0"
+    ],
+    [
+        "pytest-json",
+        "pytest-json",
+        "0.4.0"
+    ],
+    [
+        "locket",
+        "locket",
+        "0.2.1"
+    ],
+    [
+        "spvcm",
+        "spvcm",
+        "0.3.0"
+    ],
+    [
+        "jasper",
+        "jasper",
+        "0.1"
+    ],
+    [
+        "wsgiproxy2",
+        "wsgiproxy2",
+        "0.5.1"
+    ],
+    [
+        "tempora",
+        "tempora",
+        "4.1.1"
+    ],
+    [
+        "scramp",
+        "scramp",
+        "1.4.1"
+    ],
+    [
+        "idna_ssl",
+        "idna_ssl",
+        "1.1.0"
+    ],
+    [
+        "sasl",
+        "sasl",
+        "0.3.1"
+    ],
+    [
+        "backports.shutil_get_terminal_size",
+        "backports.shutil_get_terminal_size",
+        "1.0.0"
+    ],
+    [
+        "intake-parquet",
+        "intake-parquet",
+        "0.2.3"
+    ],
+    [
+        "case",
+        "case",
+        "1.5.3"
+    ],
+    [
+        "anyqt",
+        "anyqt",
+        "0.0.13"
+    ],
+    [
+        "jaraco.functools",
+        "jaraco.functools",
+        "3.3.0"
+    ],
+    [
+        "altgraph",
+        "altgraph",
+        "0.17.2"
+    ],
+    [
+        "vc",
+        "vc",
+        "2018.7.10"
+    ],
+    [
+        "libaio",
+        "libaio",
+        "0.8.3"
+    ],
+    [
+        "backports.weakref",
+        "backports.weakref",
+        "1.0.post1"
+    ],
+    [
+        "threadloop",
+        "threadloop",
+        "1.0.2"
+    ],
+    [
+        "vswhere",
+        "vswhere",
+        "1.3.0"
+    ],
+    [
+        "cmake_setuptools",
+        "cmake_setuptools",
+        "0.1.3"
+    ],
+    [
+        "docker-pycreds",
+        "docker-pycreds",
+        "0.4.0"
+    ],
+    [
+        "jaraco.text",
+        "jaraco.text",
+        "3.5.1"
+    ],
+    [
+        "grin",
+        "grin",
+        "1.3.0"
+    ],
+    [
+        "pytest-openfiles",
+        "pytest-openfiles",
+        "0.5.0"
+    ],
+    [
+        "clickhouse-cityhash",
+        "clickhouse-cityhash",
+        "1.0.2.3"
+    ],
+    [
+        "hstspreload",
+        "hstspreload",
+        "2021.10.1"
+    ],
+    [
+        "wstools",
+        "wstools",
+        "0.4.8"
+    ],
+    [
+        "backports_abc",
+        "backports_abc",
+        "0.5"
+    ],
+    [
+        "patch-ng",
+        "patch-ng",
+        "1.17.4"
+    ],
+    [
+        "certipy",
+        "certipy",
+        "0.1.3"
+    ],
+    [
+        "matplotlib-inline",
+        "matplotlib-inline",
+        "0.1.3"
+    ],
+    [
+        "googleapis-common-protos",
+        "googleapis-common-protos",
+        "1.53.0"
+    ],
+    [
+        "jaraco.collections",
+        "jaraco.collections",
+        "3.4.0"
+    ],
+    [
+        "jaraco.itertools",
+        "jaraco.itertools",
+        "6.0.3"
+    ],
+    [
+        "plaster_pastedeploy",
+        "plaster_pastedeploy",
+        "0.7"
+    ],
+    [
+        "google-crc32c",
+        "google-crc32c",
+        "1.2.0"
+    ],
+    [
+        "backports.tempfile",
+        "backports.tempfile",
+        "1.0"
+    ],
+    [
+        "db",
+        "db",
+        "0.1.1"
+    ],
+    [
+        "traceback2",
+        "traceback2",
+        "1.4.0"
+    ],
+    [
+        "xmlrpc2",
+        "xmlrpc2",
+        "0.3.1"
+    ],
+    [
+        "backports.os",
+        "backports.os",
+        "0.1.1"
+    ],
+    [
+        "intreehooks",
+        "intreehooks",
+        "1.0"
+    ],
+    [
+        "multivolumefile",
+        "multivolumefile",
+        "0.2.3"
+    ],
+    [
+        "tangled-up-in-unicode",
+        "tangled-up-in-unicode",
+        "0.2.0"
+    ],
+    [
+        "configurable-http-proxy",
+        "configurable-http-proxy",
+        "0.2.2"
+    ],
+    [
+        "python-hdfs",
+        "python-hdfs",
+        "0.4"
+    ],
+    [
+        "portend",
+        "portend",
+        "2.7.1"
+    ],
+    [
+        "pyct",
+        "pyct",
+        "0.4.8"
+    ],
+    [
+        "zope.deprecation",
+        "zope.deprecation",
+        "4.4.0"
+    ],
+    [
+        "mongo-tools",
+        "mongo-tools",
+        "0.0.2"
+    ],
+    [
+        "serverfiles",
+        "serverfiles",
+        "0.3.1"
+    ],
+    [
+        "pytest-astropy",
+        "pytest-astropy",
+        "0.9.0"
+    ],
+    [
+        "krb5",
+        "krb5",
+        "0.1.1"
+    ],
+    [
+        "jaraco.classes",
+        "jaraco.classes",
+        "3.2.1"
+    ],
+    [
+        "bottlechest",
+        "bottlechest",
+        "0.7.1"
+    ],
+    [
+        "pywin32-ctypes",
+        "pywin32-ctypes",
+        "0.2.0"
+    ],
+    [
+        "clyent",
+        "clyent",
+        "1.2.1"
+    ],
+    [
+        "yasm",
+        "yasm",
+        "0.1.0a1"
+    ],
+    [
+        "nasm",
+        "nasm",
+        "0.1.0"
+    ],
+    [
+        "mysql",
+        "mysql",
+        "0.0.3"
+    ],
+    [
+        "metis",
+        "metis",
+        "0.2a5"
+    ],
+    [
+        "pyppmd",
+        "pyppmd",
+        "0.17.0"
+    ],
+    [
+        "linecache2",
+        "linecache2",
+        "1.0.0"
+    ],
+    [
+        "assimp",
+        "assimp",
+        "4.1.4"
+    ],
+    [
+        "applaunchservices",
+        "applaunchservices",
+        "0.2.1"
+    ],
+    [
+        "cryptacular",
+        "cryptacular",
+        "1.6.2"
+    ],
+    [
+        "ghc",
+        "ghc",
+        "0.2.0"
+    ],
+    [
+        "readline",
+        "readline",
+        "6.2.4.1"
+    ],
+    [
+        "tini",
+        "tini",
+        "3.0.1"
+    ],
+    [
+        "proj",
+        "proj",
+        "0.2.0"
+    ],
+    [
+        "m4",
+        "m4",
+        "0.0.0"
+    ],
+    [
+        "gsl",
+        "gsl",
+        "0.0.3"
+    ],
+    [
+        "autoconf",
+        "autoconf",
+        "2021.9.12.1"
+    ],
+    [
+        "libuuid",
+        "libuuid",
+        "1.0.0"
+    ],
+    [
+        "gperf",
+        "gperf",
+        "0.2"
+    ],
+    [
+        "bcj-cffi",
+        "bcj-cffi",
+        "0.5.1"
+    ],
+    [
+        "wincertstore",
+        "wincertstore",
+        "0.2.1"
+    ],
+    [
+        "chainer",
+        "chainer",
+        "7.8.0"
+    ],
+    [
+        "sip",
+        "sip",
+        "6.2.0"
+    ],
+    [
+        "pycurl",
+        "pycurl",
+        "7.44.1"
+    ],
+    [
+        "lancet",
+        "lancet",
+        "0.8.1"
+    ],
+    [
+        "flake8-polyfill",
+        "flake8-polyfill",
+        "1.0.2"
+    ],
+    [
+        "dropbox",
+        "dropbox",
+        "11.21.0"
+    ],
+    [
+        "marshmallow-enum",
+        "marshmallow-enum",
+        "1.5.1"
+    ],
+    [
+        "tk",
+        "tk",
+        "0.1.0"
+    ],
+    [
+        "routes",
+        "routes",
+        "2.5.1"
+    ],
+    [
+        "repoze.lru",
+        "repoze.lru",
+        "0.7"
+    ],
+    [
+        "apache-libcloud",
+        "apache-libcloud",
+        "3.3.1"
+    ],
+    [
+        "qtconsole",
+        "qtconsole",
+        "5.1.1"
+    ],
+    [
+        "ipywidgets",
+        "ipywidgets",
+        "8.0.0a6"
+    ],
+    [
+        "thriftpy",
+        "thriftpy",
+        "0.3.9"
+    ],
+    [
+        "thrift",
+        "thrift",
+        "0.15.0"
+    ],
+    [
+        "holoviews",
+        "holoviews",
+        "1.14.6"
+    ],
+    [
+        "ant",
+        "ant",
+        "0.1.0"
+    ],
+    [
+        "jupyterlab_server",
+        "jupyterlab_server",
+        "2.8.2"
+    ],
+    [
+        "beautifulsoup4",
+        "beautifulsoup4",
+        "4.10.0"
+    ],
+    [
+        "bs4",
+        "bs4",
+        "0.0.1"
+    ],
+    [
+        "serf",
+        "serf",
+        "0.3.1"
+    ],
+    [
+        "jpeg",
+        "jpeg",
+        "0.1.4"
+    ],
+    [
+        "atk",
+        "atk",
+        "0.1.3.8b0"
+    ],
+    [
+        "meson",
+        "meson",
+        "0.59.2"
+    ],
+    [
+        "glib",
+        "glib",
+        "1.0.0"
+    ],
+    [
+        "orc",
+        "orc",
+        "0.1.0"
+    ],
+    [
+        "tapi",
+        "tapi",
+        "0.1.7"
+    ],
+    [
+        "clang",
+        "clang",
+        "11.0"
+    ],
+    [
+        "xlutils",
+        "xlutils",
+        "2.0.0"
+    ],
+    [
+        "whoosh",
+        "whoosh",
+        "2.7.4"
+    ],
+    [
+        "starkbank-ecdsa",
+        "starkbank-ecdsa",
+        "1.1.1"
+    ],
+    [
+        "sphinxcontrib-websupport",
+        "sphinxcontrib-websupport",
+        "1.2.4"
+    ],
+    [
+        "sgmllib3k",
+        "sgmllib3k",
+        "1.0.0"
+    ],
+    [
+        "segregation",
+        "segregation",
+        "2.1.0"
+    ],
+    [
+        "sas7bdat",
+        "sas7bdat",
+        "2.2.3"
+    ],
+    [
+        "rfc3986",
+        "rfc3986",
+        "1.5.0"
+    ],
+    [
+        "requests-toolbelt",
+        "requests-toolbelt",
+        "0.9.1"
+    ],
+    [
+        "pysal",
+        "pysal",
+        "2.5.0"
+    ],
+    [
+        "python-jenkins",
+        "python-jenkins",
+        "1.8.0.0a0"
+    ],
+    [
+        "pysmbclient",
+        "pysmbclient",
+        "0.1.5"
+    ],
+    [
+        "pysftp",
+        "pysftp",
+        "0.2.9"
+    ],
+    [
+        "pyqtgraph",
+        "pyqtgraph",
+        "0.12.2"
+    ],
+    [
+        "pydotplus",
+        "pydotplus",
+        "2.0.2"
+    ],
+    [
+        "pathlib",
+        "pathlib",
+        "1.0.1"
+    ],
+    [
+        "passlib",
+        "passlib",
+        "1.7.4"
+    ],
+    [
+        "orange-widget-base",
+        "orange-widget-base",
+        "4.14.1"
+    ],
+    [
+        "palettable",
+        "palettable",
+        "3.3.0"
+    ],
+    [
+        "munkres",
+        "munkres",
+        "1.1.4"
+    ],
+    [
+        "mando",
+        "mando",
+        "0.7.0"
+    ],
+    [
+        "locustio",
+        "locustio",
+        "1.0b2"
+    ],
+    [
+        "jupyter_telemetry",
+        "jupyter_telemetry",
+        "0.1.0"
+    ],
+    [
+        "inequality",
+        "inequality",
+        "1.0.0"
+    ],
+    [
+        "hsluv",
+        "hsluv",
+        "5.0.2"
+    ],
+    [
+        "heapdict",
+        "heapdict",
+        "1.0.1"
+    ],
+    [
+        "geopandas",
+        "geopandas",
+        "0.10.0"
+    ],
+    [
+        "descartes",
+        "descartes",
+        "1.1.0"
+    ],
+    [
+        "dbfread",
+        "dbfread",
+        "2.0.7"
+    ],
+    [
+        "dash-table",
+        "dash-table",
+        "5.0.0"
+    ],
+    [
+        "cram",
+        "cram",
+        "0.7"
+    ],
+    [
+        "contextlib2",
+        "contextlib2",
+        "21.6.0"
+    ],
+    [
+        "clickclick",
+        "clickclick",
+        "20.10.2"
+    ],
+    [
+        "click-didyoumean",
+        "click-didyoumean",
+        "0.3.0"
+    ],
+    [
+        "ciocheck",
+        "ciocheck",
+        "0.1.1"
+    ],
+    [
+        "agate-excel",
+        "agate-excel",
+        "0.2.5"
+    ],
+    [
+        "agate-dbf",
+        "agate-dbf",
+        "0.2.2"
+    ],
+    [
+        "distributed",
+        "distributed",
+        "2021.9.1"
+    ],
+    [
+        "lit",
+        "lit",
+        "13.0.0rc1"
+    ],
+    [
+        "ipykernel",
+        "ipykernel",
+        "6.4.1"
+    ],
+    [
+        "astropy",
+        "astropy",
+        "4.3.1"
+    ],
+    [
+        "pytest-filter-subpackage",
+        "pytest-filter-subpackage",
+        "0.1.1"
+    ],
+    [
+        "singledispatch",
+        "singledispatch",
+        "3.7.0"
+    ],
+    [
+        "regex",
+        "regex",
+        "2021.9.30"
+    ],
+    [
+        "xlwings",
+        "xlwings",
+        "0.24.9"
+    ],
+    [
+        "pygments",
+        "pygments",
+        "2.10.0"
+    ],
+    [
+        "notebook",
+        "notebook",
+        "6.4.4"
+    ],
+    [
+        "debugpy",
+        "debugpy",
+        "1.5.0"
+    ],
+    [
+        "rust",
+        "rust",
+        "0.1.1"
+    ],
+    [
+        "jupyterlab",
+        "jupyterlab",
+        "4.0.0a11"
+    ],
+    [
+        "panel",
+        "panel",
+        "0.13.0a6"
+    ],
+    [
+        "pyzmq",
+        "pyzmq",
+        "22.3.0"
+    ],
+    [
+        "jupyter-packaging",
+        "jupyter-packaging",
+        "0.10.6"
+    ],
+    [
+        "jeepney",
+        "jeepney",
+        "0.7.1"
+    ],
+    [
+        "cffi",
+        "cffi",
+        "1.15.0rc1"
+    ],
+    [
+        "tbb4py",
+        "tbb4py",
+        "2021.4.0"
+    ],
+    [
+        "tbb-devel",
+        "tbb-devel",
+        "2021.4.0"
+    ],
+    [
+        "tbb",
+        "tbb",
+        "2021.4.0"
+    ],
+    [
+        "gdal",
+        "gdal",
+        "3.3.2"
+    ],
+    [
+        "hvplot",
+        "hvplot",
+        "0.8.0a7"
+    ],
+    [
+        "intel-opencl-rt",
+        "intel-opencl-rt",
+        "2021.4.0"
+    ],
+    [
+        "spdlog",
+        "spdlog",
+        "2.0.4"
+    ],
+    [
+        "intel-fortran-rt",
+        "intel-fortran-rt",
+        "2021.4.0"
+    ],
+    [
+        "intel-cmplr-lib-rt",
+        "intel-cmplr-lib-rt",
+        "2021.4.0"
+    ],
+    [
+        "intel-cmplr-lic-rt",
+        "intel-cmplr-lic-rt",
+        "2021.4.0"
+    ],
+    [
+        "dpcpp-cpp-rt",
+        "dpcpp-cpp-rt",
+        "2021.4.0"
+    ],
+    [
+        "oneccl-devel",
+        "oneccl-devel",
+        "2021.4.0"
+    ],
+    [
+        "python-kubernetes",
+        "python-kubernetes",
+        "0.2"
+    ],
+    [
+        "cython",
+        "cython",
+        "3.0.0a9"
+    ],
+    [
+        "eventlet",
+        "eventlet",
+        "0.32.0"
+    ],
+    [
+        "mkl",
+        "mkl",
+        "2021.4.0"
+    ],
+    [
+        "mkl-include",
+        "mkl-include",
+        "2021.4.0"
+    ],
+    [
+        "intel-openmp",
+        "intel-openmp",
+        "2021.4.0"
+    ],
+    [
+        "dal",
+        "dal",
+        "0.2"
+    ],
+    [
+        "daal-static",
+        "daal-static",
+        "2021.4.0"
+    ],
+    [
+        "daal-include",
+        "daal-include",
+        "2021.4.0"
+    ],
+    [
+        "daal-devel",
+        "daal-devel",
+        "2021.4.0"
+    ],
+    [
+        "daal",
+        "daal",
+        "2021.4.0"
+    ],
+    [
+        "geoalchemy2",
+        "geoalchemy2",
+        "0.9.4"
+    ],
+    [
+        "python-debian",
+        "python-debian",
+        "0.1.40"
+    ],
+    [
+        "proto-plus",
+        "proto-plus",
+        "1.19.2"
+    ],
+    [
+        "spyder",
+        "spyder",
+        "5.1.5"
+    ],
+    [
+        "pkginfo",
+        "pkginfo",
+        "1.7.1"
+    ],
+    [
+        "invoke",
+        "invoke",
+        "1.6.0"
+    ],
+    [
+        "glue-core",
+        "glue-core",
+        "1.2.2"
+    ],
+    [
+        "pytorch",
+        "pytorch",
+        "1.0.2"
+    ],
+    [
+        "libpysal",
+        "libpysal",
+        "4.5.1"
+    ],
+    [
+        "nuitka",
+        "nuitka",
+        "0.6.17.1"
+    ],
+    [
+        "go",
+        "go",
+        "1.2.1"
+    ],
+    [
+        "ephem",
+        "ephem",
+        "4.1"
+    ],
+    [
+        "anaconda-client",
+        "anaconda-client",
+        "1.2.2"
+    ],
+    [
+        "supervisor",
+        "supervisor",
+        "4.2.2"
+    ],
+    [
+        "sphinxcontrib-htmlhelp",
+        "sphinxcontrib-htmlhelp",
+        "2.0.0"
+    ],
+    [
+        "sphinxcontrib-serializinghtml",
+        "sphinxcontrib-serializinghtml",
+        "1.1.5"
+    ],
+    [
+        "sortedcontainers",
+        "sortedcontainers",
+        "2.4.0"
+    ],
+    [
+        "scikit-rf",
+        "scikit-rf",
+        "0.19.0"
+    ],
+    [
+        "mypy",
+        "mypy",
+        "0.910"
+    ],
+    [
+        "execnet",
+        "execnet",
+        "1.9.0"
+    ],
+    [
+        "pyviz_comms",
+        "pyviz_comms",
+        "2.1.0"
+    ],
+    [
+        "datashader",
+        "datashader",
+        "0.13.0"
+    ],
+    [
+        "humanfriendly",
+        "humanfriendly",
+        "10.0"
+    ],
+    [
+        "coloredlogs",
+        "coloredlogs",
+        "15.0.1"
+    ],
+    [
+        "pyarrow",
+        "pyarrow",
+        "5.0.0"
+    ],
+    [
+        "bazel",
+        "bazel",
+        "0.0.0.20200723"
+    ],
+    [
+        "tornado",
+        "tornado",
+        "6.1"
+    ],
+    [
+        "python-lmdb",
+        "python-lmdb",
+        "1.0.0b1"
+    ],
+    [
+        "leveldb",
+        "leveldb",
+        "0.201"
+    ],
+    [
+        "h5py",
+        "h5py",
+        "3.4.0"
+    ],
+    [
+        "gensim",
+        "gensim",
+        "4.1.2"
+    ],
+    [
+        "et_xmlfile",
+        "et_xmlfile",
+        "1.1.0"
+    ],
+    [
+        "docutils",
+        "docutils",
+        "0.17.1"
+    ],
+    [
+        "cftime",
+        "cftime",
+        "1.5.1"
+    ],
+    [
+        "anaconda",
+        "anaconda",
+        "0.0.1.1"
+    ],
+    [
+        "aiofiles",
+        "aiofiles",
+        "0.7.0"
+    ],
+    [
+        "dnspython",
+        "dnspython",
+        "2.1.0"
+    ],
+    [
+        "distlib",
+        "distlib",
+        "0.3.3"
+    ],
+    [
+        "radon",
+        "radon",
+        "5.1.0"
+    ],
+    [
+        "pytest-mpi",
+        "pytest-mpi",
+        "0.5"
+    ],
+    [
+        "phik",
+        "phik",
+        "0.12.0"
+    ],
+    [
+        "pbr",
+        "pbr",
+        "5.6.0"
+    ],
+    [
+        "owslib",
+        "owslib",
+        "0.25.0"
+    ],
+    [
+        "flake8",
+        "flake8",
+        "3.9.2"
+    ],
+    [
+        "babel",
+        "babel",
+        "2.9.1"
+    ],
+    [
+        "alembic",
+        "alembic",
+        "1.7.3"
+    ],
+    [
+        "reportlab",
+        "reportlab",
+        "3.6.1"
+    ],
+    [
+        "sympy",
+        "sympy",
+        "1.9rc1"
+    ],
+    [
+        "geoviews",
+        "geoviews",
+        "1.9.2"
+    ],
+    [
+        "zfpy",
+        "zfpy",
+        "0.5.5"
+    ],
+    [
+        "zfp",
+        "zfp",
+        "0.0.0"
+    ],
+    [
+        "enum34",
+        "enum34",
+        "1.1.10"
+    ],
+    [
+        "dash-renderer",
+        "dash-renderer",
+        "1.9.1"
+    ],
+    [
+        "agate-sql",
+        "agate-sql",
+        "0.5.8"
+    ],
+    [
+        "tinycss",
+        "tinycss",
+        "0.4"
+    ],
+    [
+        "conda-content-trust",
+        "conda-content-trust",
+        "0.1.2"
+    ],
+    [
+        "nbformat",
+        "nbformat",
+        "5.1.3"
+    ],
+    [
+        "pomegranate",
+        "pomegranate",
+        "0.14.5"
+    ],
+    [
+        "constructor",
+        "constructor",
+        "0.1.0"
+    ],
+    [
+        "jupyter_console",
+        "jupyter_console",
+        "6.4.0"
+    ],
+    [
+        "numba",
+        "numba",
+        "0.54.1rc1"
+    ],
+    [
+        "libmagic",
+        "libmagic",
+        "1.0"
+    ],
+    [
+        "lxml",
+        "lxml",
+        "4.6.3"
+    ],
+    [
+        "ipympl",
+        "ipympl",
+        "0.8.0"
+    ],
+    [
+        "ruamel_yaml",
+        "ruamel_yaml",
+        "0.17.16"
+    ],
+    [
+        "nbclassic",
+        "nbclassic",
+        "0.3.2"
+    ],
+    [
+        "pyinstaller",
+        "pyinstaller",
+        "4.5.1"
+    ],
+    [
+        "paste",
+        "paste",
+        "3.5.0"
+    ],
+    [
+        "jupyter_server",
+        "jupyter_server",
+        "1.11.1"
+    ],
+    [
+        "yapf",
+        "yapf",
+        "0.31.0"
+    ],
+    [
+        "pycodestyle",
+        "pycodestyle",
+        "2.7.0"
+    ],
+    [
+        "agate",
+        "agate",
+        "1.6.3"
+    ],
+    [
+        "openpyxl",
+        "openpyxl",
+        "3.0.9"
+    ],
+    [
+        "grpcio",
+        "grpcio",
+        "1.41.0"
+    ],
+    [
+        "websocket-client",
+        "websocket-client",
+        "1.2.1"
+    ],
+    [
+        "twisted-iocpsupport",
+        "twisted-iocpsupport",
+        "1.0.2"
+    ],
+    [
+        "unidecode",
+        "unidecode",
+        "1.3.2"
+    ],
+    [
+        "aniso8601",
+        "aniso8601",
+        "9.0.1"
+    ],
+    [
+        "waf",
+        "waf",
+        "0.0.1"
+    ],
+    [
+        "sputnik",
+        "sputnik",
+        "0.9.3"
+    ],
+    [
+        "pyopengl",
+        "pyopengl",
+        "3.1.5"
+    ],
+    [
+        "fuel",
+        "fuel",
+        "0.2.0"
+    ],
+    [
+        "cvxopt",
+        "cvxopt",
+        "1.2.7"
+    ],
+    [
+        "whisper",
+        "whisper",
+        "1.1.8"
+    ],
+    [
+        "scikit-bio",
+        "scikit-bio",
+        "0.5.6"
+    ],
+    [
+        "webob",
+        "webob",
+        "1.8.7"
+    ],
+    [
+        "rsa",
+        "rsa",
+        "4.7.2"
+    ],
+    [
+        "py4j",
+        "py4j",
+        "0.10.9.2"
+    ],
+    [
+        "tempita",
+        "tempita",
+        "0.5.3.dev0"
+    ],
+    [
+        "suds-jurko",
+        "suds-jurko",
+        "0.6"
+    ],
+    [
+        "pywget",
+        "pywget",
+        "0.31"
+    ],
+    [
+        "pycairo",
+        "pycairo",
+        "1.20.1"
+    ],
+    [
+        "neon",
+        "neon",
+        "0.1.2"
+    ],
+    [
+        "grpcio-tools",
+        "grpcio-tools",
+        "1.41.0"
+    ],
+    [
+        "gevent-websocket",
+        "gevent-websocket",
+        "0.10.1"
+    ],
+    [
+        "fabric",
+        "fabric",
+        "2.6.0"
+    ],
+    [
+        "cubes",
+        "cubes",
+        "1.1"
+    ],
+    [
+        "python-daemon",
+        "python-daemon",
+        "2.3.0"
+    ],
+    [
+        "capturer",
+        "capturer",
+        "3.0"
+    ],
+    [
+        "bpython",
+        "bpython",
+        "0.21"
+    ],
+    [
+        "verboselogs",
+        "verboselogs",
+        "1.7"
+    ],
+    [
+        "venusian",
+        "venusian",
+        "3.0.0"
+    ],
+    [
+        "testscenarios",
+        "testscenarios",
+        "0.5.0"
+    ],
+    [
+        "pbkdf2",
+        "pbkdf2",
+        "1.3"
+    ],
+    [
+        "pyls-spyder",
+        "pyls-spyder",
+        "0.4.0"
+    ],
+    [
+        "ruamel.yaml.clib",
+        "ruamel.yaml.clib",
+        "0.2.6"
+    ],
+    [
+        "ruamel.yaml",
+        "ruamel.yaml",
+        "0.17.16"
+    ],
+    [
+        "grpcio-gcp",
+        "grpcio-gcp",
+        "0.2.2"
+    ],
+    [
+        "flask-restful",
+        "flask-restful",
+        "0.3.9"
+    ],
+    [
+        "termcolor",
+        "termcolor",
+        "1.1.0"
+    ],
+    [
+        "plaster",
+        "plaster",
+        "1.0"
+    ],
+    [
+        "ipyparallel",
+        "ipyparallel",
+        "7.1.0"
+    ],
+    [
+        "grako",
+        "grako",
+        "3.99.9"
+    ],
+    [
+        "expressions",
+        "expressions",
+        "0.2.3"
+    ],
+    [
+        "blist",
+        "blist",
+        "1.3.6"
+    ],
+    [
+        "urwid",
+        "urwid",
+        "2.1.2"
+    ],
+    [
+        "pykerberos",
+        "pykerberos",
+        "1.2.3.dev0"
+    ],
+    [
+        "posix_ipc",
+        "posix_ipc",
+        "1.0.5"
+    ],
+    [
+        "joblib",
+        "joblib",
+        "1.0.1"
+    ],
+    [
+        "webtest",
+        "webtest",
+        "3.0.0"
+    ],
+    [
+        "pydispatcher",
+        "pydispatcher",
+        "2.0.5"
+    ],
+    [
+        "funcsigs",
+        "funcsigs",
+        "1.0.2"
+    ],
+    [
+        "datashape",
+        "datashape",
+        "0.5.2"
+    ],
+    [
+        "characteristic",
+        "characteristic",
+        "14.3.0"
+    ],
+    [
+        "backports.shutil_which",
+        "backports.shutil_which",
+        "3.5.2"
+    ],
+    [
+        "fontconfig",
+        "fontconfig",
+        "0.1"
+    ],
+    [
+        "zict",
+        "zict",
+        "2.0.0"
+    ],
+    [
+        "pytest-pep8",
+        "pytest-pep8",
+        "1.0.6"
+    ],
+    [
+        "pytest-localserver",
+        "pytest-localserver",
+        "0.5.0"
+    ],
+    [
+        "pytest-cache",
+        "pytest-cache",
+        "1.0"
+    ],
+    [
+        "pytest-astropy-header",
+        "pytest-astropy-header",
+        "0.1.2"
+    ],
+    [
+        "jupyterlab_pygments",
+        "jupyterlab_pygments",
+        "0.1.2"
+    ],
+    [
+        "msgpack-python",
+        "msgpack-python",
+        "0.5.6"
+    ],
+    [
+        "jupyter_core",
+        "jupyter_core",
+        "4.8.1"
+    ],
+    [
+        "wasabi",
+        "wasabi",
+        "0.8.2"
+    ],
+    [
+        "pytz",
+        "pytz",
+        "2021.3"
+    ],
+    [
+        "utf8proc",
+        "utf8proc",
+        "0.0.3"
+    ],
+    [
+        "sphinxcontrib-devhelp",
+        "sphinxcontrib-devhelp",
+        "1.0.2"
+    ],
+    [
+        "sphinxcontrib-qthelp",
+        "sphinxcontrib-qthelp",
+        "1.0.3"
+    ],
+    [
+        "sphinxcontrib-jsmath",
+        "sphinxcontrib-jsmath",
+        "1.0.1"
+    ],
+    [
+        "sphinxcontrib-applehelp",
+        "sphinxcontrib-applehelp",
+        "1.0.2"
+    ],
+    [
+        "alabaster",
+        "alabaster",
+        "0.7.12"
+    ],
+    [
+        "thriftpy2",
+        "thriftpy2",
+        "0.4.14"
+    ],
+    [
+        "lockfile",
+        "lockfile",
+        "0.12.2"
+    ],
+    [
+        "icu",
+        "icu",
+        "0.0.1"
+    ],
+    [
+        "docopt",
+        "docopt",
+        "0.6.2"
+    ],
+    [
+        "boost",
+        "boost",
+        "0.1"
+    ],
+    [
+        "blinker",
+        "blinker",
+        "1.4"
+    ],
+    [
+        "anyjson",
+        "anyjson",
+        "0.3.3"
+    ],
+    [
+        "appscript",
+        "appscript",
+        "1.1.2"
+    ],
+    [
+        "sortedcollections",
+        "sortedcollections",
+        "2.1.0"
+    ],
+    [
+        "dash",
+        "dash",
+        "2.0.0"
+    ],
+    [
+        "requests_download",
+        "requests_download",
+        "0.1.2"
+    ],
+    [
+        "apptools",
+        "apptools",
+        "5.1.0"
+    ],
+    [
+        "logilab-common",
+        "logilab-common",
+        "1.8.1"
+    ],
+    [
+        "pscript",
+        "pscript",
+        "0.7.6"
+    ],
+    [
+        "pycrypto",
+        "pycrypto",
+        "2.6.1"
+    ],
+    [
+        "neotime",
+        "neotime",
+        "1.7.4"
+    ],
+    [
+        "xlrd",
+        "xlrd",
+        "2.0.1"
+    ],
+    [
+        "pyopenssl",
+        "pyopenssl",
+        "21.0.0"
+    ],
+    [
+        "access",
+        "access",
+        "1.1.3"
+    ],
+    [
+        "py",
+        "py",
+        "1.10.0"
+    ],
+    [
+        "xlwt",
+        "xlwt",
+        "1.3.0"
+    ],
+    [
+        "widgetsnbextension",
+        "widgetsnbextension",
+        "4.0.0a4"
+    ],
+    [
+        "simplegeneric",
+        "simplegeneric",
+        "0.8.1"
+    ],
+    [
+        "pytest-remotedata",
+        "pytest-remotedata",
+        "0.3.2"
+    ],
+    [
+        "pep8",
+        "pep8",
+        "1.7.1"
+    ],
+    [
+        "jupyter",
+        "jupyter",
+        "1.0.0"
+    ],
+    [
+        "future",
+        "future",
+        "0.18.2"
+    ],
+    [
+        "unittest2",
+        "unittest2",
+        "1.1.0"
+    ],
+    [
+        "three-merge",
+        "three-merge",
+        "0.1.1"
+    ],
+    [
+        "mock",
+        "mock",
+        "4.0.3"
+    ],
+    [
+        "bidict",
+        "bidict",
+        "0.21.3"
+    ],
+    [
+        "libpython",
+        "libpython",
+        "0.2"
+    ],
+    [
+        "kombu",
+        "kombu",
+        "5.2.0rc1"
+    ],
+    [
+        "pyreadline",
+        "pyreadline",
+        "2.1"
+    ],
+    [
+        "ply",
+        "ply",
+        "3.11"
+    ],
+    [
+        "python.app",
+        "python.app",
+        "0.1"
+    ],
+    [
+        "pyaudio",
+        "pyaudio",
+        "0.2.11"
+    ],
+    [
+        "nose",
+        "nose",
+        "1.3.7"
+    ],
+    [
+        "ipython_genutils",
+        "ipython_genutils",
+        "0.2.0"
+    ],
+    [
+        "pexpect",
+        "pexpect",
+        "4.8.0"
+    ],
+    [
+        "editdistance",
+        "editdistance",
+        "0.5.3"
+    ],
+    [
+        "backports",
+        "backports",
+        "1.0"
+    ],
+    [
+        "gn",
+        "gn",
+        "0.1"
+    ],
+    [
+        "numpydoc",
+        "numpydoc",
+        "1.1.0"
+    ],
+    [
+        "pyxdg",
+        "pyxdg",
+        "0.27"
+    ],
+    [
+        "freetype",
+        "freetype",
+        "0"
+    ],
+    [
+        "statistics",
+        "statistics",
+        "3.4.0b3"
+    ],
+    [
+        "libtool",
+        "libtool",
+        "1.1.0"
+    ],
+    [
+        "jupyterlab_launcher",
+        "jupyterlab_launcher",
+        "0.13.1"
+    ],
+    [
+        "glueviz",
+        "glueviz",
+        "1.2.0"
+    ],
+    [
+        "sed",
+        "sed",
+        "0.3.1"
+    ],
+    [
+        "protego",
+        "protego",
+        "0.1.16"
+    ],
+    [
+        "basemap",
+        "basemap",
+        "1.0.7"
+    ],
+    [
+        "magma",
+        "magma",
+        "0.3.9"
+    ],
+    [
+        "leather",
+        "leather",
+        "0.3.3"
+    ],
+    [
+        "orange3",
+        "orange3",
+        "3.30.1"
+    ],
+    [
+        "baycomp",
+        "baycomp",
+        "1.0.2"
+    ],
+    [
+        "snappy",
+        "snappy",
+        "3.0.1"
+    ],
+    [
+        "yajl",
+        "yajl",
+        "0.3.5"
+    ],
+    [
+        "tensorboard-plugin-wit",
+        "tensorboard-plugin-wit",
+        "1.8.0"
+    ],
+    [
+        "pynio",
+        "pynio",
+        "1.0"
+    ],
+    [
+        "nss",
+        "nss",
+        "3.8.5"
+    ],
+    [
+        "llvmmath",
+        "llvmmath",
+        "0.1.1"
+    ],
+    [
+        "pil",
+        "pil",
+        "1.1.6"
+    ],
+    [
+        "vtk",
+        "vtk",
+        "9.1.0rc1"
+    ],
+    [
+        "casuarius",
+        "casuarius",
+        "1.1"
+    ],
+    [
+        "cdecimal",
+        "cdecimal",
+        "2.3"
+    ],
+    [
+        "mercurial",
+        "mercurial",
+        "5.9.1"
+    ],
+    [
+        "cheetah",
+        "cheetah",
+        "2.4.4"
+    ],
+    [
+        "vispy",
+        "vispy",
+        "0.9.0"
+    ],
+    [
+        "blaze",
+        "blaze",
+        "0.10.1"
+    ],
+    [
+        "mysql-connector-python",
+        "mysql-connector-python",
+        "8.0.26"
+    ],
+    [
+        "faulthandler",
+        "faulthandler",
+        "3.2"
+    ],
+    [
+        "python-ntlm",
+        "python-ntlm",
+        "1.1.0"
+    ],
+    [
+        "llvmpy",
+        "llvmpy",
+        "0.12.7"
+    ],
+    [
+        "pyopengl-accelerate",
+        "pyopengl-accelerate",
+        "3.1.5"
+    ],
+    [
+        "pykit",
+        "pykit",
+        "0.2.0"
+    ],
+    [
+        "pyramid_tm",
+        "pyramid_tm",
+        "2.4"
+    ],
+    [
+        "googlecl",
+        "googlecl",
+        "0.9.12"
+    ],
+    [
+        "blockspring",
+        "blockspring",
+        "0.1.13"
+    ],
+    [
+        "msvc_runtime",
+        "msvc_runtime",
+        "14.29.30133"
+    ],
+    [
+        "mayavi",
+        "mayavi",
+        "4.7.3"
+    ],
+    [
+        "make",
+        "make",
+        "0.1.6.post2"
+    ],
+    [
+        "mathjax",
+        "mathjax",
+        "0.1.2"
+    ],
+    [
+        "theano",
+        "theano",
+        "1.0.5"
+    ],
+    [
+        "gdata",
+        "gdata",
+        "2.0.18"
+    ],
+    [
+        "libssh2",
+        "libssh2",
+        "0.0.1"
+    ],
+    [
+        "gtest",
+        "gtest",
+        "0.1.5"
+    ],
+    [
+        "pyramid_debugtoolbar",
+        "pyramid_debugtoolbar",
+        "4.9"
+    ],
+    [
+        "mtq",
+        "mtq",
+        "0.6.0"
+    ],
+    [
+        "perl",
+        "perl",
+        "1.0.0"
+    ],
+    [
+        "wxpython",
+        "wxpython",
+        "4.1.1"
+    ],
+    [
+        "h2o",
+        "h2o",
+        "3.34.0.1"
+    ],
+    [
+        "hyde",
+        "hyde",
+        "0.8.9"
+    ],
+    [
+        "wget",
+        "wget",
+        "3.2"
+    ],
+    [
+        "zipfile36",
+        "zipfile36",
+        "0.1.3"
+    ],
+    [
+        "morfessor",
+        "morfessor",
+        "2.0.6"
+    ],
+    [
+        "dis3",
+        "dis3",
+        "0.1.3"
+    ],
+    [
+        "soappy",
+        "soappy",
+        "0.12.22"
+    ],
+    [
+        "quiver_engine",
+        "quiver_engine",
+        "0.1.4.1.4"
+    ],
+    [
+        "pygraphviz",
+        "pygraphviz",
+        "1.7"
+    ],
+    [
+        "wsgiref",
+        "wsgiref",
+        "0.1.2"
+    ],
+    [
+        "uriparser",
+        "uriparser",
+        "1.0"
+    ],
+    [
+        "dask-jobqueue",
+        "dask-jobqueue",
+        "0.7.3"
+    ],
+    [
+        "dash-core-components",
+        "dash-core-components",
+        "2.0.0"
+    ],
+    [
+        "mpi",
+        "mpi",
+        "1.0.0"
+    ],
+    [
+        "selectors2",
+        "selectors2",
+        "2.0.2"
+    ],
+    [
+        "libopencv",
+        "libopencv",
+        "0.0.1"
+    ],
+    [
+        "zip",
+        "zip",
+        "0.0.2"
+    ],
+    [
+        "pyo3-pack",
+        "pyo3-pack",
+        "0.7.0b12"
+    ],
+    [
+        "glpk",
+        "glpk",
+        "0.4.6"
+    ],
+    [
+        "icc_rt",
+        "icc_rt",
+        "2020.0.133"
+    ]
+]
\ No newline at end of file
diff --git a/tests/simple_funcs.py b/tests/simple_funcs.py
index c4973558..d3085206 100644
--- a/tests/simple_funcs.py
+++ b/tests/simple_funcs.py
@@ -1,3 +1,5 @@
+__package__ = "tests"
+
 def two():
     return 2
 
diff --git a/tests/tdd_test.py b/tests/tdd_test.py
index 41c4a31b..5d6fad5a 100755
--- a/tests/tdd_test.py
+++ b/tests/tdd_test.py
@@ -6,97 +6,46 @@
     4. If it took longer than 1 second, move it to integration tests. Otherwise, move it to unit tests.
 """
 
+import io
 import os
 import sys
+from contextlib import AbstractContextManager, closing, redirect_stdout
+from unittest.mock import patch
 
-from contextlib import AbstractContextManager, closing
-from hashlib import sha256
-from pathlib import Path
-from warnings import catch_warnings,  filterwarnings, simplefilter
-
-import pytest
-from hypothesis import assume, example, given
 from hypothesis import strategies as st
-import use
-from use.hash_alphabet import JACK_as_num, hexdigest_as_JACK, is_JACK, num_as_hexdigest
-from use.pypi_model import JustUse_Info, PyPI_Project, PyPI_Release, Version
-
-not_local = "GITHUB_REF" in os.environ
-is_win = sys.platform.lower().startswith("win")
-
-
-@pytest.fixture()
-def reuse():
-    # making a completely new one each time would take ages (_registry)
-    use._using = {}
-    use._aspects = {}
-    use._reloaders = {}
-    return use
-
+from pytest import fixture, mark, raises, skip
 
-    
+is_win = sys.platform.startswith("win")
 
-def test_pypi_model():
+__package__ = "tests"
+import logging
 
-    release = PyPI_Release(
-        comment_text="test",
-        digests={"md5": "asdf"},
-        url="https://files.pythonhost",
-        ext=".whl",
-        packagetype="bdist_wheel",
-        distribution="numpy",
-        requires_python=False,
-        python_version="cp3",
-        python_tag="cp3",
-        platform_tag="cp4",
-        filename="numpy-1.19.5-cp3-cp3-cp4-bdist_wheel.whl",
-        abi_tag="cp3",
-        yanked=False,
-        version="1.19.5",
-    )
-    assert type(release)(**release.dict()) == release
+from use import auto_install, fatal_exceptions, no_cleanup, use
+from use.aspectizing import _unwrap, _wrap
 
-    info = JustUse_Info(
-        distribution="numpy",
-        version="1.19.5",
-        build_tag="cp4",
-        python_tag="cp4",
-        abi_tag="cp4",
-        platform_tag="cp4",
-        ext="whl",
-    )
-    assert type(info)(**info.dict()) == info
+log = logging.getLogger(".".join((__package__, __name__)))
+log.setLevel(logging.DEBUG if "DEBUG" in os.environ else logging.NOTSET)
 
+use.config.testing = True
 
-class ScopedCwd(AbstractContextManager):
-    def __init__(self, newcwd: Path):
-        self._oldcwd = Path.cwd()
-        self._newcwd = newcwd
-
-    def __enter__(self, *_):
-        os.chdir(self._newcwd)
-
-    def __exit__(self, *_):
-        os.chdir(self._oldcwd)
-
-
-@pytest.mark.skipif(is_win or Path.cwd().as_posix().startswith("/media/"), reason="Not enough hours in a day")
-def test_setup_py_works(reuse):
-    import subprocess
-    with ScopedCwd(Path(__file__).parent.parent):
-        result = subprocess.check_output(
-            [sys.executable, "setup.py", "--help"],
-            shell=False
-        )
-        assert result
 
+@fixture()
+def reuse():
+    """
+    Return the `use` module in a clean state for "reuse."
+
+    NOTE: making a completely new one each time would take
+    ages, due to expensive _registry setup, re-downloading
+    venv packages, etc., so if we are careful to reset any
+    additional state changes on a case-by-case basis,
+    this approach is more efficient and is the clear winner.
+    """
+    use._using.clear()
+    use.main._reloaders.clear()
+    return use
 
-@pytest.mark.skipif(not is_win or not Path.cwd().as_posix().startswith("/media/"), reason="Not enough hours in a day")
-@given(st.text())
-def test_jack(text):
-    assume(text.isprintable())
-    sha = sha256(text.encode("utf-8")).hexdigest()
-    assert sha == num_as_hexdigest(JACK_as_num(hexdigest_as_JACK(sha)))
 
+def test_test(reuse):
+    assert reuse
 
 
diff --git a/tests/unit_test.py b/tests/unit_test.py
index f3ee0fab..7de5f3cf 100755
--- a/tests/unit_test.py
+++ b/tests/unit_test.py
@@ -1,15 +1,21 @@
+import io
 import os
 import re
 import subprocess
 import sys
 import tempfile
 import warnings
-from contextlib import AbstractContextManager, closing
+from collections.abc import Callable
+from contextlib import AbstractContextManager, closing, redirect_stdout
+from datetime import datetime
 from hashlib import sha256
 from importlib.metadata import PackageNotFoundError, distribution
 from importlib.util import find_spec
 from pathlib import Path
+from subprocess import STDOUT, check_output
+from textwrap import dedent
 from threading import _shutdown_locks
+from types import ModuleType
 from unittest.mock import patch
 from warnings import catch_warnings, filterwarnings
 
@@ -20,35 +26,48 @@
 from furl import furl as URL
 from hypothesis import assume, example, given
 from hypothesis import strategies as st
+from pytest import fixture, mark, raises, skip
 
 is_win = sys.platform.startswith("win")
 
 __package__ = "tests"
 import logging
 
-import use
-from use.hash_alphabet import JACK_as_num, hexdigest_as_JACK, is_JACK, num_as_hexdigest
+from use import auto_install, fatal_exceptions, no_cleanup, use
+from use.aspectizing import _unwrap, _wrap
+from use.hash_alphabet import (JACK_as_num, hexdigest_as_JACK, is_JACK,
+                               num_as_hexdigest)
+from use.pimp import _parse_name
+from use.pydantics import JustUse_Info, PyPI_Project, PyPI_Release, Version
 
 log = logging.getLogger(".".join((__package__, __name__)))
 log.setLevel(logging.DEBUG if "DEBUG" in os.environ else logging.NOTSET)
 
-use.config["testing"] = True
+use.config.testing = True
+use.config.no_browser = True
 
 # this is actually a test!
 from tests.simple_funcs import three
 
 
-@pytest.fixture()
+@fixture()
 def reuse():
-    # making a completely new one each time would take ages (_registry)
-    use._using = {}
-    use._aspects = {}
-    use._reloaders = {}
+    """
+    Return the `use` module in a clean state for "reuse."
+
+    NOTE: making a completely new one each time would take
+    ages, due to expensive _registry setup, re-downloading
+    venv packages, etc., so if we are careful to reset any
+    additional state changes on a case-by-case basis,
+    this approach is more efficient and is the clear winner.
+    """
+    use._using.clear()
+    use.main._reloaders.clear()
     return use
 
 
 def test_access_to_home(reuse):
-    test = reuse.Path.home() / ".justuse-python/packages/test"
+    test = reuse.config.packages / "test"
     test.touch(mode=0o644, exist_ok=True)
     with open(test, "w") as file:
         file.write("test")
@@ -58,12 +77,12 @@ def test_access_to_home(reuse):
 
 
 def test_other_case(reuse):
-    with pytest.raises(NotImplementedError):
+    with raises(NotImplementedError):
         reuse(2, modes=reuse.fatal_exceptions)
 
 
 def test_fail_dir(reuse):
-    with pytest.raises(ImportError):
+    with raises(ImportError):
         reuse(Path(""))
 
 
@@ -123,28 +142,28 @@ def test_classical_install_no_version(reuse):
 
 
 def test_PEBKAC_hash_no_version(reuse):
-    with pytest.raises(RuntimeWarning):
+    with raises(RuntimeWarning):
         reuse(
             "pytest",
-            hashes="asdf",
+            hashes="addf",
             modes=reuse.auto_install,
         )
 
 
 def test_PEBKAC_nonexisting_pkg(reuse):
     # non-existing pkg
-    with pytest.raises(ImportError):
+    with raises(ImportError):
         reuse(
             "4-^df",
             modes=reuse.auto_install,
             version="0.0.1",
-            hashes="asdf",
+            hashes="addf",
         )
 
 
 def test_PEBKAC_impossible_version(reuse):
     # impossible version
-    with pytest.raises(TypeError):  # version must be either str or tuple
+    with raises(TypeError):  # version must be either str or tuple
         reuse(
             "pytest",
             modes=reuse.auto_install,
@@ -156,11 +175,11 @@ def test_PEBKAC_impossible_version(reuse):
 def test_autoinstall_PEBKAC(reuse):
     with patch("webbrowser.open"):
         # auto-install requested, but no version or hashes specified
-        with pytest.raises(RuntimeWarning):
+        with raises(RuntimeWarning):
             reuse("pytest", modes=reuse.auto_install)
 
         # forgot hashes
-        with pytest.raises(packaging.version.InvalidVersion):
+        with raises(packaging.version.InvalidVersion):
             reuse("pytest", version="-1", modes=reuse.auto_install)
 
 
@@ -176,7 +195,7 @@ def test_version_warning(reuse):
 def test_use_global_install(reuse):
     from . import foo
 
-    with pytest.raises(NameError):
+    with raises(NameError):
         foo.bar()
 
     reuse.install()
@@ -258,8 +277,7 @@ def test_is_version_satisfied(reuse):
 
 
 def test_find_windows_artifact(reuse):
-    data = reuse._get_package_data("protobuf")
-    assert reuse.Version("3.17.3") in reuse._get_package_data("protobuf").releases
+    assert reuse.Version("3.17.3") in reuse._get_data_from_pypi(package_name="protobuf").releases
 
 
 def test_classic_import_same_version(reuse):
@@ -285,28 +303,6 @@ def test_classic_import_diff_version(reuse):
     assert w[0].category == use.VersionWarning
 
 
-@pytest.mark.skipif(True, reason="Not working, needs investigation")
-def test_use_ugrade_version_warning(reuse):
-    version = "0.0.0"
-    with warnings.catch_warnings(record=True) as w:
-        filterwarnings(action="always", module="use")
-        # no other way to change __version__ before the actual import while the version check happens on import
-        test_use = reuse(
-            reuse.Path(reuse.__file__).absolute(),
-            initial_globals={
-                "test_version": version,
-                "test_config": {"version_warning": True},
-            },
-        )
-        assert (
-            reuse.Version(test_use.test_version)
-            == reuse.Version(test_use.__version__)
-            == reuse.Version(version)
-        )
-    assert len(w) != 0
-    assert w[0].category == use.VersionWarning
-
-
 class Restorer:
     def __enter__(self):
         self.locks = set(_shutdown_locks)
@@ -316,32 +312,38 @@ def __exit__(self, arg1, arg2, arg3):
             lock.release()
 
 
-@pytest.mark.skipif(is_win, reason="windows reloading")
 def test_reloading(reuse):
     fd, file = tempfile.mkstemp(".py", "test_module")
     with Restorer():
         mod = None
         newfile = f"{file}.t"
         for check in range(1):
+            if sys.platform[:3] == "win":
+                newfile = file
             with open(newfile, "w") as f:
                 f.write(f"def foo(): return {check}")
                 f.flush()
-            os.rename(newfile, file)
+            if sys.platform[:3] != "win":
+                os.rename(newfile, file)
             mod = mod or reuse(Path(file), modes=reuse.reloading)
             while mod.foo() < check:
                 pass
 
 
 def test_suggestion_works(reuse):
-    with patch("webbrowser.open"):
+    name = "package-example"
+    with patch("webbrowser.open"), io.StringIO() as buf, redirect_stdout(buf):
+        try:
+            mod = reuse(name, modes=reuse.auto_install)
+            assert False, f"Actually returned mod: {mod}"
+        except RuntimeWarning:
+            version = buf.getvalue().splitlines()[-1].strip()
         try:
-            mod = reuse("example-pypi-package/examplepy", modes=reuse.auto_install)
+            mod = reuse(name, version=version, modes=reuse.auto_install)
             assert False, f"Actually returned mod: {mod}"
-        except (RuntimeWarning, RuntimeError) as rw:
-            last_line = rw.args[0].strip().splitlines()[-1]
-            log.info("Using last line as suggested artifact: %s", repr(last_line))
-            mod = eval(last_line)
-            log.info("suggest artifact returning: %s", mod)
+        except RuntimeWarning:
+            recommended_hash = buf.getvalue().splitlines()[-1].strip()
+        mod = reuse(name, version=version, hashes={recommended_hash}, modes=reuse.auto_install)
         assert mod
 
 
@@ -350,7 +352,7 @@ def test_clear_registry(reuse):
     try:
         fd, file = tempfile.mkstemp(".db", "test_registry")
         with closing(open(fd, "rb")):
-            reuse.registry = reuse._set_up_registry(Path(file))
+            reuse.registry = reuse._set_up_registry(path=Path(file))
             reuse.cleanup()
     finally:
         reuse.registry = reuse._set_up_registry()
@@ -358,22 +360,22 @@ def test_clear_registry(reuse):
 
 def installed_or_skip(reuse, name, version=None):
     if not (spec := find_spec(name)):
-        pytest.skip(f"{name} not installed")
+        skip(f"{name} not installed")
         return False
     try:
         dist = distribution(spec.name)
     except PackageNotFoundError as pnfe:
-        pytest.skip(f"{name} partially installed: {spec=}, {pnfe}")
+        skip(f"{name} partially installed: {spec=}, {pnfe}")
 
     if not (
         (ver := dist.metadata["version"]) and (not version or reuse.Version(ver) == reuse.Version(version))
     ):
-        pytest.skip(f"found '{name}' v{ver}, but require v{version}")
+        skip(f"found '{name}' v{ver}, but require v{version}")
         return False
     return True
 
 
-@pytest.mark.parametrize(
+@mark.parametrize(
     "name, version, hashes",
     (
         (
@@ -386,12 +388,14 @@ def installed_or_skip(reuse, name, version=None):
             "6.2.4",
             {"91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"},
         ),
-        # ("pytest-cov", "2.12.1"),
-        # ("pytest-env", "0.6.2"),
-        # ("requests", "2.24.0"),
-        # ("furl", "2.1.2"),
-        # ("wheel", "0.36.2"),
-        # ("icontract", "2.5.4"),
+        (
+            "pytest-cov",
+            "2.12.1",
+            {
+                "261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7",
+                "261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a",
+            },
+        ),
     ),
 )
 def test_85_pywt_jupyter_ubuntu_case1010(reuse, name, version, hashes):
@@ -399,40 +403,15 @@ def test_85_pywt_jupyter_ubuntu_case1010(reuse, name, version, hashes):
     In jupyter (Lubuntu VM):
     pywt = use("pywt", version="1.1.1")
     """
-    # TODO
     if not installed_or_skip(reuse, name, version):
         return
     mod = reuse(name, version=reuse.Version(version))
     assert mod
 
 
-@pytest.mark.parametrize(
-    "name, version, hashes",
-    (
-        (
-            "pytest",
-            "6.2.5",
-            {"7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
-        ),
-    ),
-)
-def test_86_numpy_case1011(reuse, name, version, hashes):
-    """Can't use("numpy", version="1.20.0", modes=use.auto_install)
-
-    on windows, py39:
-    use("numpy", version="1.20.0", auto_install=True)
-    """
-    if not installed_or_skip(reuse, name, version):
-        return
-    mod = use(
-        name,
-        version=reuse.Version(version),
-        hashes=hashes,
-        modes=use.auto_install,
-    )
-    assert (reuse.Version(modver) if (modver := reuse._get_version(mod=mod)) else version) == reuse.Version(
-        version
-    )
+def test_387_usepath_filename(reuse):
+    mod = use(use.Path(".file_for_test387.py"))
+    assert mod
 
 
 def test_hash_alphabet():
@@ -452,13 +431,12 @@ def __exit__(self, *_):
         os.chdir(self._oldcwd)
 
 
-@pytest.mark.skipif(is_win, reason="Windows TODO")
 def test_read_wheel_metadata(reuse):
-    bytes = requests.get(
+    content = requests.get(
         "https://files.pythonhosted.org/packages/45/80/cdf0df938fe63457f636d859499f4aab3d0411a90fd9472ad720a0b7eab6/justuse-0.5.0.tar.gz"
     ).content
     file = Path(tempfile.mkstemp(".tar.gz", "justuse-0.5.0")[1])
-    file.write_bytes(bytes)
+    file.write_bytes(content)
     whl_path = file
     if whl_path.exists():
         assert whl_path.exists()
@@ -470,4 +448,256 @@ def test_read_wheel_metadata(reuse):
 
 
 def test_383_use_name(reuse):
-    assert use("pprint").pprint([1, 2, 3]) is None
\ No newline at end of file
+    assert use("pprint").pprint([1, 2, 3]) is None
+
+
+def test_use_version_upgrade_warning(reuse):
+    version = reuse.Version("0.0.0")
+    srcdir = reuse.Path(reuse.__spec__.origin).parent.parent
+    with ScopedCwd(srcdir):
+        output = check_output(
+            [
+                sys.executable,
+                "-c",
+                dedent(
+                    f"""
+                    import os
+                    os.environ['USE_VERSION'] = '{version!s}'
+                    import use
+                    """
+                ),
+            ],
+            encoding="utf-8",
+            shell=False,
+            stderr=STDOUT,
+        )
+        match = re.search(r"(?P<category>[a-zA-Z_]+): " r"(?:(?!\d).)* (?P<version>\d+\.\d+\.\d+)", output)
+        assert match
+        assert match.group("category") == use.VersionWarning.__name__
+        assert match.group("version") == str(version)
+
+
+def test_fraction_of_day(reuse):
+    assert reuse.fraction_of_day(datetime(2020, 1, 1)) == 0.0
+    assert reuse.fraction_of_day(datetime(2020, 1, 1, 12)) == 500.0
+    assert reuse.fraction_of_day(datetime(2020, 1, 1, 18)) == 750.0
+    assert reuse.fraction_of_day(datetime(2020, 1, 1, 12, 30, 30)) == 521.180556
+    assert reuse.fraction_of_day(datetime(2020, 1, 1, 12, 30, 30, 90000)) == 521.181597
+
+
+def test_nirvana(reuse):
+    with raises(reuse.NirvanaWarning):
+        reuse()
+
+
+@given(st.text())
+@example("1t")
+def test_jack(text):
+    assume(text.isprintable())
+    sha = sha256(text.encode("utf-8")).hexdigest()
+    assert sha == num_as_hexdigest(JACK_as_num(hexdigest_as_JACK(sha)))
+
+
+def test_pypi_model():
+    release = PyPI_Release(
+        comment_text="test",
+        digests={"md5": "asdf"},
+        url="https://files.pythonhost",
+        ext=".whl",
+        packagetype="bdist_wheel",
+        distribution="numpy",
+        requires_python=False,
+        python_version="cp3",
+        python_tag="cp3",
+        platform_tag="cp4",
+        filename="numpy-1.19.5-cp3-cp3-cp4-bdist_wheel.whl",
+        abi_tag="cp3",
+        yanked=False,
+        version="1.19.5",
+    )
+    assert type(release)(**release.dict()) == release
+
+    info = JustUse_Info(
+        distribution="numpy",
+        version="1.19.5",
+        build_tag="cp4",
+        python_tag="cp4",
+        abi_tag="cp4",
+        platform_tag="cp4",
+        ext="whl",
+    )
+    assert type(info)(**info.dict()) == info
+
+
+def test_setup_py_works(reuse):
+    with ScopedCwd(Path(__file__).parent.parent):
+        result = subprocess.check_output([sys.executable, "setup.py", "--help"], shell=False)
+        assert result
+
+
+def test_443_py_test(reuse):
+    try:
+        imported = "py" in sys.modules
+        import py.test
+
+        if not imported:
+            del sys.modules["py"]
+    except ImportError:
+        skip("py.test is not installed")
+        return
+    mod = use("py.test")
+    assert mod
+
+
+def test_441_discord(reuse):
+    try:
+        imported = "discord" in sys.modules
+        import discord
+
+        if not imported:
+            del sys.modules["discord"]
+    except ImportError:
+        skip("discord is not installed")
+        return
+    mod = use("discord")
+    assert mod
+
+
+def test_441_discord(reuse):
+    try:
+        imported = "discord" in sys.modules
+        import discord
+
+        if not imported:
+            del sys.modules["discord"]
+    except ImportError:
+        skip("discord is not installed")
+        return
+    mod = use("discord")
+    assert mod
+
+
+@mark.parametrize(
+    "name,expected",
+    [("", (None, None)), ("foo", ("foo", "foo")), ("foo/bar", ("foo", "bar")), ("foo.py", ("foo", "foo.py"))],
+)
+def test_parse_name(name, expected):
+    assert _parse_name(name) == expected
+
+
+def test_51_sqlalchemy_failure_default_to_none(reuse):
+    mod = use(
+        "sqlalchemy",
+        version="0.7.1",
+        hashes={"45df54adf"},  # the real thing takes 12 sec to run, way too long for a test
+        # SQLAlchemy-0.7.1.tar.gz (2.3 MB) - only a single artifact
+        # Uploaded Jun 5, 2011 source
+        # but it's bugged on import - it's looking for time.clock(), which isn't a thing (anymore)
+        modes=auto_install | no_cleanup,
+        default=None,
+    )
+    assert mod is None
+
+
+@mark.parametrize(
+    "package_name, module_name, version, hashes",
+    (
+        (
+            "numpy",
+            "numpy",
+            "1.21.0",
+            {
+                "K孈諺搓稞㭯函攢訦冁舏在偦鸕覘延䅣微",  # cp39-macosx_10_14_x86_64
+                "W余跒䌹㔟倠咥膆酐䄩物绁䣹萦䱒䀒嵲㫀",  # cp310-manylinux_2_17_x86_64.manylinux2014_x86_64
+                "U燊仓虑觖䢾䪍㼞䙼⑾鮹爠䂸泫驊鏓慥脲",  # cp310-macosx_10_14_x86_64
+                "P䂹瀑蚜萃暧㡩栅訉慯忲磵殣怊伟躂岜㠽",  # cp39-manylinux_2_17_aarch64.manylinux2014_aarch64
+                "S驈烽堶堢苩觌宣陖阆㦯葅霙䇣锽鸋㻔摌",  # cp310-macosx_11_0_arm64
+                "鉳䥩猉罺䢢䚘㞐朤体颛軀辗﨏䍜圑蒷塶",  # cp310-win_amd64
+                "N䥐昺飂薏嵋㻝阫鎴蝧愩蚓臞敼鯩冓蝸銒",  # cp310-manylinux_2_17_aarch64.manylinux2014_aarch64
+                "M㐸丮䫈䩷ɽ燃䄻混蛳稝鉇役泃宵艞顃㼵",  # cp38-manylinux_2_17_x86_64.manylinux2014_x86_64
+                "l䭌㻧䈘樉䎜胵賡秖磗伳玌鄵㝷乺ʜ䩗㞄",  # cp39-win32
+                "jǛ湄閈䌣鮴䥎堃㛓笸埩眣庚捡鈕㞻鍇錭",  # cp38-win32
+                "O珌㡾㷗鴥拁嵹㒞㔹鐝Ȼ扡庇甄臞摉鮝厐",  # cp38-manylinux_2_17_aarch64.manylinux2014_aarch64
+                "蕧胸瓲工䛹侒囜第䞢棖頟䗯锩㠋㞭谶㥚",  # cp38-win_amd64
+                "P钉殯㓜魴壬谚蝇弄鈻馇蕔祏绿佡謊濔弡",  # cp39-win_amd64
+                "k蜆蓭鑶禬茌犿违維郈盇肀根棿嶠纭恦㗼",  # cp310-win32
+                "Z序套䁳暙鐁鎨樫姡鳵曧頲㲿槤誐笷楆㖑",  # pp38-manylinux_2_17_x86_64.manylinux2014_x86_64
+                "J䘻鏤熆钣吚檭䟆颃稆㥌绊錽乸棈獒轧䐥",  # cp38-macosx_10_14_x86_64
+                "k胨唃嫟鶛睉銤噶㝅婡䲍逪萻㲛撁佘籧趏",  # cp38-macosx_11_0_arm64
+                "k頃跊ó銋訋簴冴蛞铘诪韚秂坿櫹围㾶歆",  # cp39-macosx_11_0_arm64
+                "VƂ㫰雲巠䝲偯鷔餙䬸歏匧漃祌轘夽㑐㵿",  # cp39-manylinux_2_17_x86_64.manylinux2014_x86_64
+            },
+        ),
+    ),
+)
+def test_specific_packages(reuse, package_name, module_name, version, hashes):
+    """This covers #448 and all other issues that relate to specific packages and versions.
+
+    Consider this a regression test against specific reported bugs,
+    a minimal sample from the mass test.
+    """
+    mod = reuse((package_name, module_name), version=version, hashes=hashes, modes=auto_install | no_cleanup)
+    assert isinstance(mod, ModuleType)
+
+
+def test_451_ignore_spaces_in_hashes(reuse):
+    # single hash
+    mod = use("package-example", version="0.1", hashes="Y復㝿浯䨩䩯鷛㬉鼵爔滥哫鷕逮 愁墕萮緩", modes=auto_install | no_cleanup)
+    assert mod
+    del mod
+    # hash list
+    mod = use(
+        "package-example", version="0.1", hashes={"Y復㝿浯䨩䩯鷛㬉鼵爔滥哫鷕逮 愁墕萮緩"}, modes=auto_install | no_cleanup
+    )
+    assert mod
+    del mod
+
+
+def f(x):
+    return x**2
+
+
+def test_decorate(reuse):
+    def decorator(func):
+        def wrapper(x):
+            return func(x + 1)
+
+        return wrapper
+
+    _wrap(thing=sys.modules[__name__], obj=f, decorator=decorator, name="f")
+    assert f(3) == 16
+    _unwrap(thing=sys.modules[__name__], name="f")
+    assert f(3) == 9
+
+
+def test_454_bad_metadata(reuse):
+    name = "pyinputplus"
+    with patch("webbrowser.open"), io.StringIO() as buf, redirect_stdout(buf):
+        try:
+            reuse(name, modes=reuse.auto_install)
+        except RuntimeWarning:
+            version = buf.getvalue().splitlines()[-1].strip()
+        try:
+            reuse(name, version=version, modes=reuse.auto_install)
+        except RuntimeWarning:
+            recommended_hash = buf.getvalue().splitlines()[-1].strip()
+        mod = reuse(
+            name, version=version, hashes={recommended_hash}, modes=reuse.auto_install | reuse.no_cleanup
+        )
+        assert mod
+
+
+def test_454_no_tags(reuse):
+    """No version and no platform tags given - a surprisingly common issue."""
+
+    version = "3.1.2"
+    name = "pyspark"
+    with patch("webbrowser.open"), io.StringIO() as buf, redirect_stdout(buf):
+        try:
+            reuse(name, version=version, modes=reuse.auto_install)
+        except RuntimeWarning:
+            recommended_hash = buf.getvalue().splitlines()[-1].strip()
+        mod = reuse(
+            name, version=version, hashes={recommended_hash}, modes=reuse.auto_install | reuse.no_cleanup
+        )
+        assert mod