spin 0.0.1.dev0__tar.gz → 0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spin-0.0.1.dev0 → spin-0.2}/LICENSE +1 -1
- spin-0.2/PKG-INFO +41 -0
- spin-0.2/README.md +140 -0
- spin-0.2/pyproject.toml +32 -0
- spin-0.2/setup.cfg +4 -0
- spin-0.2/spin/__init__.py +9 -0
- spin-0.2/spin/__main__.py +123 -0
- spin-0.2/spin/cmds/__init__.py +8 -0
- spin-0.2/spin/cmds/build.py +9 -0
- spin-0.2/spin/cmds/meson.py +250 -0
- spin-0.2/spin/cmds/util.py +80 -0
- spin-0.2/spin/color_format.py +51 -0
- spin-0.2/spin/sectioned_help.py +21 -0
- spin-0.2/spin/tests/test_meson.py +100 -0
- spin-0.2/spin.egg-info/PKG-INFO +41 -0
- spin-0.2/spin.egg-info/SOURCES.txt +17 -0
- spin-0.2/spin.egg-info/dependency_links.txt +1 -0
- spin-0.2/spin.egg-info/requires.txt +8 -0
- spin-0.2/spin.egg-info/top_level.txt +1 -0
- spin-0.0.1.dev0/PKG-INFO +0 -83
- spin-0.0.1.dev0/README.md +0 -56
- spin-0.0.1.dev0/pyproject.toml +0 -92
- spin-0.0.1.dev0/spin/__init__.py +0 -7
- spin-0.0.1.dev0/spin/pl/__init__.py +0 -1
- spin-0.0.1.dev0/spin/pl/basic.py +0 -7
- spin-0.0.1.dev0/spin/pp/__init__.py +0 -1
- spin-0.0.1.dev0/spin/pp/basic.py +0 -7
- spin-0.0.1.dev0/spin/tl/__init__.py +0 -1
- spin-0.0.1.dev0/spin/tl/basic.py +0 -7
spin-0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: spin
|
|
3
|
+
Version: 0.2
|
|
4
|
+
Summary: Developer tool for scientific Python libraries
|
|
5
|
+
Maintainer-email: Scientific Python <spin@discuss.scientific-python.org>
|
|
6
|
+
License: BSD 3-Clause License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2021--2022, Scientific Python project
|
|
9
|
+
All rights reserved.
|
|
10
|
+
|
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
|
13
|
+
|
|
14
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
15
|
+
list of conditions and the following disclaimer.
|
|
16
|
+
|
|
17
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
18
|
+
this list of conditions and the following disclaimer in the documentation
|
|
19
|
+
and/or other materials provided with the distribution.
|
|
20
|
+
|
|
21
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
22
|
+
contributors may be used to endorse or promote products derived from
|
|
23
|
+
this software without specific prior written permission.
|
|
24
|
+
|
|
25
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
26
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
27
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
28
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
29
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
30
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
31
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
32
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
33
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
|
+
|
|
36
|
+
Project-URL: homepage, https://github.com/scientific-python/spin
|
|
37
|
+
Classifier: Development Status :: 4 - Beta
|
|
38
|
+
Classifier: Programming Language :: Python
|
|
39
|
+
Requires-Python: >=3.7
|
|
40
|
+
Provides-Extra: lint
|
|
41
|
+
License-File: LICENSE
|
spin-0.2/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# 💫 Scientific Python INcantations (`spin`)
|
|
2
|
+
|
|
3
|
+
## A developer tool for scientific Python libraries
|
|
4
|
+
|
|
5
|
+
**NOTE:** If you are looking for `devpy`, this is it! We had to rename
|
|
6
|
+
the package to publish it on PyPi.
|
|
7
|
+
|
|
8
|
+
Developers need to memorize a whole bunch of magic command-line incantations.
|
|
9
|
+
And these incantations change from time to time!
|
|
10
|
+
Typically, their lives are made simpler by a Makefile, but Makefiles can be convoluted, are not written in Python, and are hard to extend.
|
|
11
|
+
The rationale behind `spin` is therefore to provide a simple interface for common development tasks.
|
|
12
|
+
It comes with a few common build commands out the box, but can easily be customized per project.
|
|
13
|
+
|
|
14
|
+
As a curiosity: the impetus behind developing the tool was the mass migration of scientific Python libraries (SciPy, scikit-image, and NumPy, etc.) to Meson, after distutils was deprecated.
|
|
15
|
+
When many of the build and installation commands changed, it made sense to abstract away the nuisance of having to re-learn them.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
`spin` is not yet available via PyPi (but soon!).
|
|
20
|
+
|
|
21
|
+
Meanwhile, please install it directly from GitHub:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
pip install git+https://github.com/scientific-python/spin
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
Settings are in your project's `pyproject.toml`.
|
|
30
|
+
As an example, see the `[tool.spin]` section of [an example `pyproject.toml`](https://github.com/scientific-python/spin/blob/main/example_pkg/pyproject.toml).
|
|
31
|
+
|
|
32
|
+
The `[tool.spin]` section should contain:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
package = "pkg_importname" # name of your package
|
|
36
|
+
commands = [
|
|
37
|
+
"spin.cmds.meson.build",
|
|
38
|
+
"spin.cmds.meson.test"
|
|
39
|
+
]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
See [the command selection](#built-in-commands) below.
|
|
43
|
+
|
|
44
|
+
### Command sections
|
|
45
|
+
|
|
46
|
+
Once you have several commands, it may be useful to organize them into sections.
|
|
47
|
+
In `pyproject.toml`, instead of specifying the commands as a list, use the following structure:
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[tool.spin.commands]
|
|
51
|
+
"Build" = [
|
|
52
|
+
"spin.cmds.meson.build",
|
|
53
|
+
"spin.cmds.meson.test"
|
|
54
|
+
]
|
|
55
|
+
"Environments" = [
|
|
56
|
+
"spin.cmds.meson.shell",
|
|
57
|
+
"spin.cmds.meson.ipython",
|
|
58
|
+
"spin.cmds.meson.python"
|
|
59
|
+
]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
These commands will then be rendered as:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Build:
|
|
66
|
+
build 🔧 Build package with Meson/ninja and install
|
|
67
|
+
test 🔧 Run tests
|
|
68
|
+
|
|
69
|
+
Environments:
|
|
70
|
+
shell 💻 Launch shell with PYTHONPATH set
|
|
71
|
+
ipython 💻 Launch IPython shell with PYTHONPATH set
|
|
72
|
+
python 🐍 Launch Python shell with PYTHONPATH set
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Running
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
python -m spin
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
On Unix-like systems, you can also copy the [`dev.py` script](https://github.com/scientific-python/spin/blob/main/example_pkg/dev.py) to the root of your project directory, and launch it as:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
./dev.py
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Built-in commands
|
|
88
|
+
|
|
89
|
+
### [Meson](https://meson-python.readthedocs.io)
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
build 🔧 Build package with Meson/ninja and install to `build-install`
|
|
93
|
+
ipython 💻 Launch IPython shell with PYTHONPATH set
|
|
94
|
+
python 🐍 Launch Python shell with PYTHONPATH set
|
|
95
|
+
shell 💻 Launch shell with PYTHONPATH set
|
|
96
|
+
test 🔧 Run pytest
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### [Build](https://pypa-build.readthedocs.io/en/stable/) (PEP 517 builder)
|
|
100
|
+
|
|
101
|
+
`spin` was started with Meson in mind, but we're working on expanding commands for PEP 517 `build`.
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
sdist 📦 Build a source distribution in `dist/`.
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 🧪 Custom commands
|
|
108
|
+
|
|
109
|
+
`spin` can invoke custom commands. These commands define their own arguments, and have access to the `pyproject.toml` file for further configuration.
|
|
110
|
+
|
|
111
|
+
See, e.g., the [example custom command](https://github.com/scientific-python/spin/blob/main/example_pkg/.spin/cmds.py).
|
|
112
|
+
|
|
113
|
+
Add custom commands to the `commands` variable in the `[tool.spin]` section of `pyproject.toml` as follows:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
commands = [..., '.spin/cmds.py:example']
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Here, the command is stored in `.spin/cmds.py`, and the function
|
|
120
|
+
is named `example`.
|
|
121
|
+
|
|
122
|
+
### Configuration
|
|
123
|
+
|
|
124
|
+
Custom commands can access the `pyproject.toml` as follows:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from spin import util
|
|
128
|
+
|
|
129
|
+
@click.command()
|
|
130
|
+
def example():
|
|
131
|
+
"""Command that accesses `pyproject.toml` configuration"""
|
|
132
|
+
config = util.get_config()
|
|
133
|
+
print(config["tool.spin"])
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## History
|
|
137
|
+
|
|
138
|
+
The `dev.py` tool was [proposed for SciPy](https://github.com/scipy/scipy/issues/15489) by Ralf Gommers and [implemented](https://github.com/scipy/scipy/pull/15959) by Sayantika Banik, Eduardo Naufel Schettino, and Ralf Gommers (also see [Sayantika's blog post](https://labs.quansight.org/blog/the-evolution-of-the-scipy-developer-cli)).
|
|
139
|
+
Inspired by that implementation, `spin` (this package) is a minimal rewrite by Stéfan van der Walt, that aims to be easily extendable so that it can be used across ecosystem libraries.
|
|
140
|
+
We thank Danila Bredikhin and Luca Marconato who kindly donated the `spin` name on PyPi.
|
spin-0.2/pyproject.toml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "spin"
|
|
3
|
+
version = "0.2"
|
|
4
|
+
requires-python = ">=3.7"
|
|
5
|
+
description = "Developer tool for scientific Python libraries"
|
|
6
|
+
license = {file = "LICENSE"}
|
|
7
|
+
maintainers = [
|
|
8
|
+
{name = "Scientific Python", email = "spin@discuss.scientific-python.org"}
|
|
9
|
+
]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Programming Language :: Python"
|
|
13
|
+
]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"click",
|
|
16
|
+
"toml",
|
|
17
|
+
"colorama; platform_system == 'Windows'"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
lint = ["pre-commit >= 3.1"]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
homepage = "https://github.com/scientific-python/spin"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
include = ["spin*"]
|
|
28
|
+
|
|
29
|
+
[tool.pytest.ini_options]
|
|
30
|
+
filterwarnings = [
|
|
31
|
+
"ignore:cannot collect 'test' because it is not a function:",
|
|
32
|
+
]
|
spin-0.2/setup.cfg
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import collections
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import importlib
|
|
5
|
+
from glob import glob
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import toml
|
|
9
|
+
|
|
10
|
+
from . import cmds
|
|
11
|
+
from .cmds import util
|
|
12
|
+
from .cmds import *
|
|
13
|
+
from .sectioned_help import SectionedHelpGroup
|
|
14
|
+
from .color_format import ColorHelpFormatter
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
click.Context.formatter_class = ColorHelpFormatter
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DotDict(collections.UserDict):
|
|
21
|
+
def __getitem__(self, key):
|
|
22
|
+
subitem = self.data
|
|
23
|
+
for subkey in key.split("."):
|
|
24
|
+
try:
|
|
25
|
+
subitem = subitem[subkey]
|
|
26
|
+
except KeyError:
|
|
27
|
+
raise KeyError(f"`{key}` not found in configuration") from None
|
|
28
|
+
return subitem
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
if not os.path.exists("pyproject.toml"):
|
|
33
|
+
print("Error: cannot find [pyproject.toml]")
|
|
34
|
+
sys.exit(1)
|
|
35
|
+
|
|
36
|
+
with open("pyproject.toml") as f:
|
|
37
|
+
try:
|
|
38
|
+
toml_config = toml.load(f)
|
|
39
|
+
except:
|
|
40
|
+
print("Cannot parse [pyproject.toml]")
|
|
41
|
+
sys.exit(1)
|
|
42
|
+
|
|
43
|
+
project_config = toml_config.get("project", {})
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
config = toml_config["tool"]["spin"]
|
|
47
|
+
except KeyError:
|
|
48
|
+
print("No configuration found in [pyproject.toml] for [tool.spin]")
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
proj_name = project_config.get("name", config["package"])
|
|
52
|
+
|
|
53
|
+
@click.group(help=f"Developer tool for {proj_name}", cls=SectionedHelpGroup)
|
|
54
|
+
@click.pass_context
|
|
55
|
+
def group(ctx):
|
|
56
|
+
ctx.meta["config"] = DotDict(toml_config)
|
|
57
|
+
ctx.meta["commands"] = ctx.command.section_commands
|
|
58
|
+
ctx.show_default = True
|
|
59
|
+
|
|
60
|
+
config_cmds = config["commands"]
|
|
61
|
+
# Commands can be provided as a list, or as a dictionary
|
|
62
|
+
# so that they can be sorted into sections
|
|
63
|
+
if isinstance(config_cmds, list):
|
|
64
|
+
config_cmds = {"Commands": config_cmds}
|
|
65
|
+
|
|
66
|
+
# Backward compatibility workaround
|
|
67
|
+
# Originally, you could specify any of these commands as `spin.cmd`
|
|
68
|
+
# and we'd fetch it from util
|
|
69
|
+
commands = {
|
|
70
|
+
"spin.build": cmds.meson.build,
|
|
71
|
+
"spin.test": cmds.meson.test,
|
|
72
|
+
"spin.ipython": cmds.meson.ipython,
|
|
73
|
+
"spin.python": cmds.meson.python,
|
|
74
|
+
"spin.shell": cmds.meson.shell,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for section, cmds in config_cmds.items():
|
|
78
|
+
for cmd in cmds:
|
|
79
|
+
if cmd not in commands:
|
|
80
|
+
# First, see if we can directly import the command
|
|
81
|
+
if not ":" in cmd:
|
|
82
|
+
path, func = cmd.rsplit(".", maxsplit=1)
|
|
83
|
+
try:
|
|
84
|
+
mod = importlib.import_module(path)
|
|
85
|
+
except ImportError:
|
|
86
|
+
print(
|
|
87
|
+
f"!! Could not import module `{path}` to load command `{cmd}`"
|
|
88
|
+
)
|
|
89
|
+
continue
|
|
90
|
+
else:
|
|
91
|
+
try:
|
|
92
|
+
path, func = cmd.split(":")
|
|
93
|
+
spec = importlib.util.spec_from_file_location(
|
|
94
|
+
"custom_mod", path
|
|
95
|
+
)
|
|
96
|
+
mod = importlib.util.module_from_spec(spec)
|
|
97
|
+
spec.loader.exec_module(mod)
|
|
98
|
+
except FileNotFoundError:
|
|
99
|
+
print(
|
|
100
|
+
f"!! Could not find file `{path}` to load custom command `{cmd}`.\n"
|
|
101
|
+
)
|
|
102
|
+
continue
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(
|
|
105
|
+
f"!! Could not import file `{path}` to load custom command `{cmd}`.\n"
|
|
106
|
+
)
|
|
107
|
+
raise e
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
cmd_func = getattr(mod, func)
|
|
111
|
+
except AttributeError:
|
|
112
|
+
print(f"!! Could not load command `{func}` from file `{path}`.\n")
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
commands[cmd] = cmd_func
|
|
116
|
+
|
|
117
|
+
group.add_command(commands[cmd], section=section)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
group()
|
|
121
|
+
except Exception as e:
|
|
122
|
+
print(f"{e}; aborting.")
|
|
123
|
+
sys.exit(1)
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import shutil
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from .util import run, get_config, get_commands
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
install_dir = "build-install"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _set_pythonpath():
|
|
15
|
+
site_packages = _get_site_packages()
|
|
16
|
+
env = os.environ
|
|
17
|
+
|
|
18
|
+
if "PYTHONPATH" in env:
|
|
19
|
+
env["PYTHONPATH"] = f"{site_packages}{os.pathsep}{env['PYTHONPATH']}"
|
|
20
|
+
else:
|
|
21
|
+
env["PYTHONPATH"] = site_packages
|
|
22
|
+
|
|
23
|
+
return env["PYTHONPATH"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_site_packages():
|
|
27
|
+
candidate_paths = []
|
|
28
|
+
for root, dirs, files in os.walk(install_dir):
|
|
29
|
+
for subdir in dirs:
|
|
30
|
+
if subdir == "site-packages" or subdir == "dist-packages":
|
|
31
|
+
candidate_paths.append(os.path.abspath(os.path.join(root, subdir)))
|
|
32
|
+
|
|
33
|
+
X, Y = sys.version_info.major, sys.version_info.minor
|
|
34
|
+
|
|
35
|
+
site_packages = None
|
|
36
|
+
if any(f"python{X}." in p for p in candidate_paths):
|
|
37
|
+
# We have a system that uses `python3.X/site-packages` or `python3.X/dist-packages`
|
|
38
|
+
site_packages = [p for p in candidate_paths if f"python{X}.{Y}" in p]
|
|
39
|
+
if len(site_packages) == 0:
|
|
40
|
+
raise FileNotFoundError(
|
|
41
|
+
f"No site-packages found in {install_dir} for Python {X}.{Y}"
|
|
42
|
+
)
|
|
43
|
+
else:
|
|
44
|
+
site_packages = site_packages[0]
|
|
45
|
+
else:
|
|
46
|
+
# A naming scheme that does not encode the Python major/minor version is used, so return
|
|
47
|
+
# whatever site-packages path was found
|
|
48
|
+
if len(candidate_paths) > 1:
|
|
49
|
+
raise FileNotFoundError(
|
|
50
|
+
f"Multiple `site-packages` found in `{install_dir}`, but cannot use Python version to disambiguate"
|
|
51
|
+
)
|
|
52
|
+
elif len(candidate_paths) == 1:
|
|
53
|
+
site_packages = candidate_paths[0]
|
|
54
|
+
|
|
55
|
+
if site_packages is None:
|
|
56
|
+
raise FileNotFoundError(
|
|
57
|
+
f"No `site-packages` or `dist-packages` found under `{install_dir}`"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return site_packages
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _meson_version():
|
|
64
|
+
try:
|
|
65
|
+
p = run(["meson", "--version"], output=False, echo=False)
|
|
66
|
+
return p.stdout.decode("ascii").strip()
|
|
67
|
+
except:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _meson_version_configured():
|
|
72
|
+
try:
|
|
73
|
+
meson_info_fn = os.path.join("build", "meson-info", "meson-info.json")
|
|
74
|
+
meson_info = json.load(open(meson_info_fn))
|
|
75
|
+
return meson_info["meson_version"]["full"]
|
|
76
|
+
except:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@click.command()
|
|
81
|
+
@click.option("-j", "--jobs", help="Number of parallel tasks to launch", type=int)
|
|
82
|
+
@click.option("--clean", is_flag=True, help="Clean build directory before build")
|
|
83
|
+
@click.option(
|
|
84
|
+
"-v", "--verbose", is_flag=True, help="Print all build output, even installation"
|
|
85
|
+
)
|
|
86
|
+
@click.argument("meson_args", nargs=-1)
|
|
87
|
+
def build(meson_args, jobs=None, clean=False, verbose=False):
|
|
88
|
+
"""🔧 Build package with Meson/ninja and install
|
|
89
|
+
|
|
90
|
+
MESON_ARGS are passed through e.g.:
|
|
91
|
+
|
|
92
|
+
./dev.py build -- -Dpkg_config_path=/lib64/pkgconfig
|
|
93
|
+
|
|
94
|
+
The package is installed to build-install
|
|
95
|
+
|
|
96
|
+
By default builds for release, to be able to use a debugger set CFLAGS
|
|
97
|
+
appropriately. For example, for linux use
|
|
98
|
+
|
|
99
|
+
CFLAGS="-O0 -g" ./dev.py build
|
|
100
|
+
"""
|
|
101
|
+
build_dir = "build"
|
|
102
|
+
setup_cmd = ["meson", "setup", build_dir, "--prefix=/usr"] + list(meson_args)
|
|
103
|
+
|
|
104
|
+
if clean:
|
|
105
|
+
print(f"Removing `{build_dir}`")
|
|
106
|
+
if os.path.isdir(build_dir):
|
|
107
|
+
shutil.rmtree(build_dir)
|
|
108
|
+
print(f"Removing `{install_dir}`")
|
|
109
|
+
if os.path.isdir(install_dir):
|
|
110
|
+
shutil.rmtree(install_dir)
|
|
111
|
+
|
|
112
|
+
if not (os.path.exists(build_dir) and _meson_version_configured()):
|
|
113
|
+
p = run(setup_cmd, sys_exit=False)
|
|
114
|
+
if p.returncode != 0:
|
|
115
|
+
raise RuntimeError(
|
|
116
|
+
"Meson configuration failed; please try `dev.py build` again with the `--clean` flag."
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
# Build dir has been configured; check if it was configured by
|
|
120
|
+
# current version of Meson
|
|
121
|
+
|
|
122
|
+
if _meson_version() != _meson_version_configured():
|
|
123
|
+
run(setup_cmd + ["--reconfigure"])
|
|
124
|
+
|
|
125
|
+
# Any other conditions that warrant a reconfigure?
|
|
126
|
+
|
|
127
|
+
p = run(["meson", "compile", "-C", build_dir], sys_exit=False)
|
|
128
|
+
p = run(
|
|
129
|
+
[
|
|
130
|
+
"meson",
|
|
131
|
+
"install",
|
|
132
|
+
"--only-changed",
|
|
133
|
+
"-C",
|
|
134
|
+
build_dir,
|
|
135
|
+
"--destdir",
|
|
136
|
+
f"../{install_dir}",
|
|
137
|
+
],
|
|
138
|
+
output=verbose,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@click.command()
|
|
143
|
+
@click.argument("pytest_args", nargs=-1)
|
|
144
|
+
@click.pass_context
|
|
145
|
+
def test(ctx, pytest_args):
|
|
146
|
+
"""🔧 Run tests
|
|
147
|
+
|
|
148
|
+
PYTEST_ARGS are passed through directly to pytest, e.g.:
|
|
149
|
+
|
|
150
|
+
./dev.py test -- -v
|
|
151
|
+
|
|
152
|
+
By default, runs the full test suite. To skip "slow" tests, run
|
|
153
|
+
|
|
154
|
+
./dev.py test -- -m "not slow"
|
|
155
|
+
"""
|
|
156
|
+
cfg = get_config()
|
|
157
|
+
|
|
158
|
+
command_groups = get_commands()
|
|
159
|
+
commands = [cmd for section in command_groups for cmd in command_groups[section]]
|
|
160
|
+
build_cmd = next((cmd for cmd in commands if cmd.name == "build"), None)
|
|
161
|
+
if build_cmd:
|
|
162
|
+
click.secho(
|
|
163
|
+
f"Invoking `build` prior to running tests:", bold=True, fg="bright_green"
|
|
164
|
+
)
|
|
165
|
+
ctx.invoke(build_cmd)
|
|
166
|
+
|
|
167
|
+
if not pytest_args:
|
|
168
|
+
pytest_args = (cfg.get("tool.spin.package", None),)
|
|
169
|
+
if pytest_args == (None,):
|
|
170
|
+
print(
|
|
171
|
+
"Please specify `package = packagename` under `tool.spin` section of `pyproject.toml`"
|
|
172
|
+
)
|
|
173
|
+
sys.exit(1)
|
|
174
|
+
|
|
175
|
+
site_path = _get_site_packages()
|
|
176
|
+
_set_pythonpath()
|
|
177
|
+
|
|
178
|
+
print(f'$ export PYTHONPATH="{site_path}"')
|
|
179
|
+
run(
|
|
180
|
+
[sys.executable, "-m", "pytest", f"--rootdir={site_path}"] + list(pytest_args),
|
|
181
|
+
cwd=site_path,
|
|
182
|
+
replace=True,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@click.command()
|
|
187
|
+
@click.argument("ipython_args", nargs=-1)
|
|
188
|
+
def ipython(ipython_args):
|
|
189
|
+
"""💻 Launch IPython shell with PYTHONPATH set
|
|
190
|
+
|
|
191
|
+
IPYTHON_ARGS are passed through directly to IPython, e.g.:
|
|
192
|
+
|
|
193
|
+
./dev.py ipython -- -i myscript.py
|
|
194
|
+
"""
|
|
195
|
+
p = _set_pythonpath()
|
|
196
|
+
print(f'💻 Launching IPython with PYTHONPATH="{p}"')
|
|
197
|
+
run(["ipython", "--ignore-cwd"] + list(ipython_args), replace=True)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@click.command()
|
|
201
|
+
@click.argument("shell_args", nargs=-1)
|
|
202
|
+
def shell(shell_args=[]):
|
|
203
|
+
"""💻 Launch shell with PYTHONPATH set
|
|
204
|
+
|
|
205
|
+
SHELL_ARGS are passed through directly to the shell, e.g.:
|
|
206
|
+
|
|
207
|
+
./dev.py shell -- -c 'echo $PYTHONPATH'
|
|
208
|
+
|
|
209
|
+
Ensure that your shell init file (e.g., ~/.zshrc) does not override
|
|
210
|
+
the PYTHONPATH.
|
|
211
|
+
"""
|
|
212
|
+
p = _set_pythonpath()
|
|
213
|
+
shell = os.environ.get("SHELL", "sh")
|
|
214
|
+
cmd = [shell] + list(shell_args)
|
|
215
|
+
print(f'💻 Launching shell with PYTHONPATH="{p}"')
|
|
216
|
+
print(f"⚠ Change directory to avoid importing source instead of built package")
|
|
217
|
+
print(f"⚠ Ensure that your ~/.shellrc does not unset PYTHONPATH")
|
|
218
|
+
run(cmd, replace=True)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@click.command()
|
|
222
|
+
@click.argument("python_args", nargs=-1)
|
|
223
|
+
def python(python_args):
|
|
224
|
+
"""🐍 Launch Python shell with PYTHONPATH set
|
|
225
|
+
|
|
226
|
+
PYTHON_ARGS are passed through directly to Python, e.g.:
|
|
227
|
+
|
|
228
|
+
./dev.py python -- -c 'import sys; print(sys.path)'
|
|
229
|
+
"""
|
|
230
|
+
p = _set_pythonpath()
|
|
231
|
+
v = sys.version_info
|
|
232
|
+
if (v.major < 3) or (v.major == 3 and v.minor < 11):
|
|
233
|
+
print("We're sorry, but this feature only works on Python 3.11 and greater 😢")
|
|
234
|
+
print()
|
|
235
|
+
print(
|
|
236
|
+
"Why? Because we need the '-P' flag so the interpreter doesn't muck with PYTHONPATH"
|
|
237
|
+
)
|
|
238
|
+
print()
|
|
239
|
+
print("However! You can still launch your own interpreter:")
|
|
240
|
+
print()
|
|
241
|
+
print(f" PYTHONPATH='{p}' python")
|
|
242
|
+
print()
|
|
243
|
+
print("And then call:")
|
|
244
|
+
print()
|
|
245
|
+
print("import sys; del(sys.path[0])")
|
|
246
|
+
sys.exit(-1)
|
|
247
|
+
|
|
248
|
+
print(f'🐍 Launching Python with PYTHONPATH="{p}"')
|
|
249
|
+
|
|
250
|
+
run(["/usr/bin/env", "python", "-P"] + list(python_args), replace=True)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import subprocess
|
|
4
|
+
import shlex
|
|
5
|
+
import sysconfig
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run(
|
|
12
|
+
cmd, cwd=None, replace=False, sys_exit=True, output=True, echo=True, *args, **kwargs
|
|
13
|
+
):
|
|
14
|
+
"""Run a shell command.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
cwd : str
|
|
19
|
+
Change to this directory before execution.
|
|
20
|
+
replace : bool
|
|
21
|
+
Whether to replace the current process.
|
|
22
|
+
sys_exit : bool
|
|
23
|
+
Whether to exit if the shell command returns with error != 0.
|
|
24
|
+
output : bool
|
|
25
|
+
Whether to display output as the process runs.
|
|
26
|
+
If set to ``False``, can be accessed afterwards as
|
|
27
|
+
``p.stdout``.
|
|
28
|
+
If `sys_exit` is True and the process fails, output is printed
|
|
29
|
+
regardless.
|
|
30
|
+
echo : bool
|
|
31
|
+
Whether or not to echo commands.
|
|
32
|
+
|
|
33
|
+
Other arguments and keywords are passed directly to `subprocess.run`.
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
if cwd:
|
|
37
|
+
if echo:
|
|
38
|
+
click.secho(f"$ cd {cwd}", bold=True, fg="bright_blue")
|
|
39
|
+
os.chdir(cwd)
|
|
40
|
+
|
|
41
|
+
cmdstr = " ".join(shlex.quote(arg) for arg in cmd)
|
|
42
|
+
if echo:
|
|
43
|
+
click.secho(f"$ {cmdstr}", bold=True, fg="bright_blue")
|
|
44
|
+
|
|
45
|
+
if output is False:
|
|
46
|
+
output_kwargs = {"stdout": subprocess.PIPE, "stderr": subprocess.STDOUT}
|
|
47
|
+
kwargs = {**output_kwargs, **kwargs}
|
|
48
|
+
|
|
49
|
+
if replace:
|
|
50
|
+
os.execvp(cmd[0], cmd)
|
|
51
|
+
print(f"Failed to launch `{cmd}`")
|
|
52
|
+
sys.exit(-1)
|
|
53
|
+
else:
|
|
54
|
+
p = subprocess.run(cmd, *args, **kwargs)
|
|
55
|
+
if p.returncode != 0 and sys_exit:
|
|
56
|
+
# Output was suppressed, but the process failed, so print it anyway
|
|
57
|
+
if output is False:
|
|
58
|
+
print(p.stdout.decode("utf-8"), end="")
|
|
59
|
+
sys.exit(p.returncode)
|
|
60
|
+
return p
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_config():
|
|
64
|
+
"""Return the pyproject.toml as a dictionary."""
|
|
65
|
+
return click.get_current_context().meta["config"]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_commands():
|
|
69
|
+
"""Return a list of all commands.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
cmds : dict
|
|
74
|
+
The keys of the dictionary are the section names, the values
|
|
75
|
+
lists of commands.
|
|
76
|
+
|
|
77
|
+
If no sections are provided, all commands are listed under the
|
|
78
|
+
``commands`` key.
|
|
79
|
+
"""
|
|
80
|
+
return click.get_current_context().meta["commands"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RegexpFormatter:
|
|
8
|
+
def __init__(self, rules, default=None):
|
|
9
|
+
if default is None:
|
|
10
|
+
default = {}
|
|
11
|
+
self.rules = {re.compile(expr): format for (expr, format) in rules.items()}
|
|
12
|
+
self.default_style = click.style("", **default, reset=False)
|
|
13
|
+
self.reset_style = click.style("", reset=True)
|
|
14
|
+
|
|
15
|
+
def __call__(self, txt):
|
|
16
|
+
for expr, format in self.rules.items():
|
|
17
|
+
txt = expr.sub(click.style("\\g<0>", **format) + self.default_style, txt)
|
|
18
|
+
txt = self.default_style + txt + self.reset_style
|
|
19
|
+
return txt
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ColorHelpFormatter(click.formatting.HelpFormatter):
|
|
23
|
+
def __init__(self, *args, **kwargs):
|
|
24
|
+
kwargs["width"] = shutil.get_terminal_size()[0]
|
|
25
|
+
super().__init__(*args, **kwargs)
|
|
26
|
+
|
|
27
|
+
def write_heading(self, heading):
|
|
28
|
+
super().write_heading(click.style(heading, dim=True))
|
|
29
|
+
|
|
30
|
+
def write_dl(self, items):
|
|
31
|
+
"""Print definition list"""
|
|
32
|
+
key_fmt = RegexpFormatter(
|
|
33
|
+
{
|
|
34
|
+
" [A-Z]+": {"bold": True, "fg": "yellow"},
|
|
35
|
+
r"\-[a-z]{1}(?=[ ,]{1})": {"fg": "green"},
|
|
36
|
+
},
|
|
37
|
+
default={"bold": True, "fg": "cyan"},
|
|
38
|
+
)
|
|
39
|
+
val_fmt = RegexpFormatter({r"\[default: .*?\]": {"dim": True}})
|
|
40
|
+
items = [(key_fmt(key), val_fmt(val)) for (key, val) in items]
|
|
41
|
+
super().write_dl(items)
|
|
42
|
+
|
|
43
|
+
def write_usage(self, prog, args="", prefix=None):
|
|
44
|
+
fmt = RegexpFormatter(
|
|
45
|
+
{r"(?<=\[)[A-Z_]+(?=\])": {"fg": "cyan"}}, default={"bold": True}
|
|
46
|
+
)
|
|
47
|
+
super().write_usage(
|
|
48
|
+
click.style(prog, bold=True, fg="white"),
|
|
49
|
+
click.style(fmt(args), bold=True),
|
|
50
|
+
prefix=click.style("Usage: ", bold=True, fg="yellow"),
|
|
51
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import collections
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SectionedHelpGroup(click.Group):
|
|
6
|
+
"""Organize commands as sections"""
|
|
7
|
+
|
|
8
|
+
def __init__(self, *args, **kwargs):
|
|
9
|
+
self.section_commands = collections.defaultdict(list)
|
|
10
|
+
super().__init__(*args, **kwargs)
|
|
11
|
+
|
|
12
|
+
def add_command(self, cmd, name=None, section=None):
|
|
13
|
+
self.section_commands[section].append(cmd)
|
|
14
|
+
super().add_command(cmd, name=name)
|
|
15
|
+
|
|
16
|
+
def format_commands(self, ctx, formatter):
|
|
17
|
+
for group, cmds in self.section_commands.items():
|
|
18
|
+
with formatter.section(group):
|
|
19
|
+
formatter.write_dl(
|
|
20
|
+
[(cmd.name, cmd.get_short_help_str() or "") for cmd in cmds]
|
|
21
|
+
)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
from os.path import join as pjoin, normpath
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from spin.cmds import meson
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def make_paths(root, paths):
|
|
12
|
+
for p in paths:
|
|
13
|
+
os.makedirs(pjoin(root, p.lstrip("/")))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_path_discovery(monkeypatch):
|
|
17
|
+
version = sys.version_info
|
|
18
|
+
X, Y = version.major, version.minor
|
|
19
|
+
|
|
20
|
+
# With multiple site-packages, choose the one that
|
|
21
|
+
# matches the current Python version
|
|
22
|
+
with tempfile.TemporaryDirectory() as d:
|
|
23
|
+
with monkeypatch.context() as m:
|
|
24
|
+
install_dir = pjoin(d, "build-install")
|
|
25
|
+
m.setattr(meson, "install_dir", install_dir)
|
|
26
|
+
|
|
27
|
+
make_paths(
|
|
28
|
+
install_dir,
|
|
29
|
+
[
|
|
30
|
+
f"/usr/lib64/python{X}.{Y}/site-packages",
|
|
31
|
+
f"/usr/lib64/python{X}.{Y + 1}/site-packages",
|
|
32
|
+
f"/usr/lib64/python{X}.{Y + 2}/site-packages",
|
|
33
|
+
],
|
|
34
|
+
)
|
|
35
|
+
assert (
|
|
36
|
+
normpath(f"/usr/lib64/python{X}.{Y}/site-packages")
|
|
37
|
+
in meson._get_site_packages()
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Debian uses dist-packages
|
|
41
|
+
with tempfile.TemporaryDirectory() as d:
|
|
42
|
+
with monkeypatch.context() as m:
|
|
43
|
+
install_dir = pjoin(d, "build-install")
|
|
44
|
+
m.setattr(meson, "install_dir", install_dir)
|
|
45
|
+
|
|
46
|
+
make_paths(
|
|
47
|
+
install_dir,
|
|
48
|
+
[
|
|
49
|
+
f"/usr/lib64/python{X}.{Y}/dist-packages",
|
|
50
|
+
],
|
|
51
|
+
)
|
|
52
|
+
assert (
|
|
53
|
+
normpath(f"/usr/lib64/python{X}.{Y}/dist-packages")
|
|
54
|
+
in meson._get_site_packages()
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# If there is no version information in site-packages,
|
|
58
|
+
# use whatever site-packages can be found
|
|
59
|
+
with tempfile.TemporaryDirectory() as d:
|
|
60
|
+
with monkeypatch.context() as m:
|
|
61
|
+
install_dir = pjoin(d, "build-install")
|
|
62
|
+
m.setattr(meson, "install_dir", install_dir)
|
|
63
|
+
|
|
64
|
+
make_paths(install_dir, ["/Python3/site-packages"])
|
|
65
|
+
assert normpath("/Python3/site-packages") in meson._get_site_packages()
|
|
66
|
+
|
|
67
|
+
# Raise if no site-package directory present
|
|
68
|
+
with tempfile.TemporaryDirectory() as d:
|
|
69
|
+
with monkeypatch.context() as m:
|
|
70
|
+
install_dir = pjoin(d, "build-install")
|
|
71
|
+
m.setattr(meson, "install_dir", install_dir)
|
|
72
|
+
|
|
73
|
+
with pytest.raises(FileNotFoundError):
|
|
74
|
+
meson._get_site_packages()
|
|
75
|
+
|
|
76
|
+
# If there are multiple site-package paths, but without version information,
|
|
77
|
+
# refuse the temptation to guess
|
|
78
|
+
with tempfile.TemporaryDirectory() as d:
|
|
79
|
+
install_dir = pjoin(d, "build-install")
|
|
80
|
+
make_paths(
|
|
81
|
+
install_dir, [f"/Python3/x/site-packages", f"/Python3/y/site-packages"]
|
|
82
|
+
)
|
|
83
|
+
with pytest.raises(FileNotFoundError):
|
|
84
|
+
meson._get_site_packages()
|
|
85
|
+
|
|
86
|
+
# Multiple site-package paths found, but none that matches our Python
|
|
87
|
+
with tempfile.TemporaryDirectory() as d:
|
|
88
|
+
with monkeypatch.context() as m:
|
|
89
|
+
install_dir = pjoin(d, "build-install")
|
|
90
|
+
m.setattr(meson, "install_dir", install_dir)
|
|
91
|
+
|
|
92
|
+
make_paths(
|
|
93
|
+
install_dir,
|
|
94
|
+
[
|
|
95
|
+
f"/usr/lib64/python{X}.{Y + 1}/site-packages",
|
|
96
|
+
f"/usr/lib64/python{X}.{Y + 2}/site-packages",
|
|
97
|
+
],
|
|
98
|
+
)
|
|
99
|
+
with pytest.raises(FileNotFoundError):
|
|
100
|
+
meson._get_site_packages()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: spin
|
|
3
|
+
Version: 0.2
|
|
4
|
+
Summary: Developer tool for scientific Python libraries
|
|
5
|
+
Maintainer-email: Scientific Python <spin@discuss.scientific-python.org>
|
|
6
|
+
License: BSD 3-Clause License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2021--2022, Scientific Python project
|
|
9
|
+
All rights reserved.
|
|
10
|
+
|
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
|
13
|
+
|
|
14
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
15
|
+
list of conditions and the following disclaimer.
|
|
16
|
+
|
|
17
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
18
|
+
this list of conditions and the following disclaimer in the documentation
|
|
19
|
+
and/or other materials provided with the distribution.
|
|
20
|
+
|
|
21
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
22
|
+
contributors may be used to endorse or promote products derived from
|
|
23
|
+
this software without specific prior written permission.
|
|
24
|
+
|
|
25
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
26
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
27
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
28
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
29
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
30
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
31
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
32
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
33
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
|
+
|
|
36
|
+
Project-URL: homepage, https://github.com/scientific-python/spin
|
|
37
|
+
Classifier: Development Status :: 4 - Beta
|
|
38
|
+
Classifier: Programming Language :: Python
|
|
39
|
+
Requires-Python: >=3.7
|
|
40
|
+
Provides-Extra: lint
|
|
41
|
+
License-File: LICENSE
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
spin/__init__.py
|
|
5
|
+
spin/__main__.py
|
|
6
|
+
spin/color_format.py
|
|
7
|
+
spin/sectioned_help.py
|
|
8
|
+
spin.egg-info/PKG-INFO
|
|
9
|
+
spin.egg-info/SOURCES.txt
|
|
10
|
+
spin.egg-info/dependency_links.txt
|
|
11
|
+
spin.egg-info/requires.txt
|
|
12
|
+
spin.egg-info/top_level.txt
|
|
13
|
+
spin/cmds/__init__.py
|
|
14
|
+
spin/cmds/build.py
|
|
15
|
+
spin/cmds/meson.py
|
|
16
|
+
spin/cmds/util.py
|
|
17
|
+
spin/tests/test_meson.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
spin
|
spin-0.0.1.dev0/PKG-INFO
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: spin
|
|
3
|
-
Version: 0.0.1.dev0
|
|
4
|
-
Summary: Spatial omics analysis
|
|
5
|
-
Author: Danila Bredikhin
|
|
6
|
-
Maintainer-email: Danila Bredikhin <danila.bredikhin@embl.de>
|
|
7
|
-
Requires-Python: >=3.8
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: anndata
|
|
10
|
-
Requires-Dist: bump2version ; extra == "dev"
|
|
11
|
-
Requires-Dist: sphinx>=4 ; extra == "doc"
|
|
12
|
-
Requires-Dist: furo ; extra == "doc"
|
|
13
|
-
Requires-Dist: myst-parser ; extra == "doc"
|
|
14
|
-
Requires-Dist: sphinxcontrib-bibtex>=1.0.0 ; extra == "doc"
|
|
15
|
-
Requires-Dist: scanpydoc[typehints]>=0.7.4 ; extra == "doc"
|
|
16
|
-
Requires-Dist: nbsphinx ; extra == "doc"
|
|
17
|
-
Requires-Dist: ipython>=8.3.0 ; extra == "doc"
|
|
18
|
-
Requires-Dist: pytest ; extra == "test"
|
|
19
|
-
Requires-Dist: pytest-cov ; extra == "test"
|
|
20
|
-
Project-URL: Documentation, https://spin.readthedocs.io/
|
|
21
|
-
Project-URL: Home-page, https://github.com/gtca/spin
|
|
22
|
-
Project-URL: Source, https://github.com/gtca/spin
|
|
23
|
-
Provides-Extra: dev
|
|
24
|
-
Provides-Extra: doc
|
|
25
|
-
Provides-Extra: test
|
|
26
|
-
|
|
27
|
-
# spin
|
|
28
|
-
|
|
29
|
-
[![Tests][badge-tests]][link-tests]
|
|
30
|
-
[![Documentation][badge-docs]][link-docs]
|
|
31
|
-
|
|
32
|
-
[badge-tests]: https://img.shields.io/github/workflow/status/gtca/spin/Test/main
|
|
33
|
-
[link-tests]: https://github.com/gtca/spin/actions/workflows/test.yml
|
|
34
|
-
[badge-docs]: https://img.shields.io/readthedocs/spin
|
|
35
|
-
|
|
36
|
-
Spatial omics analysis
|
|
37
|
-
|
|
38
|
-
## Getting started
|
|
39
|
-
|
|
40
|
-
Please refer to the [documentation][link-docs]. In particular, the
|
|
41
|
-
|
|
42
|
-
- [API documentation][link-api].
|
|
43
|
-
|
|
44
|
-
## Installation
|
|
45
|
-
|
|
46
|
-
You need to have Python 3.8 or newer installed on your system. If you don't have
|
|
47
|
-
Python installed, we recommend installing `Miniconda <https://docs.conda.io/en/latest/miniconda.html>`\_.
|
|
48
|
-
|
|
49
|
-
There are several alternative options to install spin:
|
|
50
|
-
|
|
51
|
-
<!--
|
|
52
|
-
1) Install the latest release of `spin` from `PyPI <https://pypi.org/project/spin/>`_:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
pip install spin
|
|
56
|
-
```
|
|
57
|
-
-->
|
|
58
|
-
|
|
59
|
-
1. Install the latest development version:
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
pip install git+https://github.com/gtca/spin.git@main
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Release notes
|
|
66
|
-
|
|
67
|
-
See the [changelog][changelog].
|
|
68
|
-
|
|
69
|
-
## Contact
|
|
70
|
-
|
|
71
|
-
For questions and help requests, you can reach out in the [scverse discourse][scverse-discourse].
|
|
72
|
-
If you found a bug, please use the [issue tracker][issue-tracker].
|
|
73
|
-
|
|
74
|
-
## Citation
|
|
75
|
-
|
|
76
|
-
> t.b.a
|
|
77
|
-
|
|
78
|
-
[scverse-discourse]: https://discourse.scverse.org/
|
|
79
|
-
[issue-tracker]: https://github.com/gtca/spin/issues
|
|
80
|
-
[changelog]: https://spin.readthedocs.io/latest/changelog.html
|
|
81
|
-
[link-docs]: https://spin.readthedocs.io
|
|
82
|
-
[link-api]: https://spin.readthedocs.io/latest/api.html
|
|
83
|
-
|
spin-0.0.1.dev0/README.md
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# spin
|
|
2
|
-
|
|
3
|
-
[![Tests][badge-tests]][link-tests]
|
|
4
|
-
[![Documentation][badge-docs]][link-docs]
|
|
5
|
-
|
|
6
|
-
[badge-tests]: https://img.shields.io/github/workflow/status/gtca/spin/Test/main
|
|
7
|
-
[link-tests]: https://github.com/gtca/spin/actions/workflows/test.yml
|
|
8
|
-
[badge-docs]: https://img.shields.io/readthedocs/spin
|
|
9
|
-
|
|
10
|
-
Spatial omics analysis
|
|
11
|
-
|
|
12
|
-
## Getting started
|
|
13
|
-
|
|
14
|
-
Please refer to the [documentation][link-docs]. In particular, the
|
|
15
|
-
|
|
16
|
-
- [API documentation][link-api].
|
|
17
|
-
|
|
18
|
-
## Installation
|
|
19
|
-
|
|
20
|
-
You need to have Python 3.8 or newer installed on your system. If you don't have
|
|
21
|
-
Python installed, we recommend installing `Miniconda <https://docs.conda.io/en/latest/miniconda.html>`\_.
|
|
22
|
-
|
|
23
|
-
There are several alternative options to install spin:
|
|
24
|
-
|
|
25
|
-
<!--
|
|
26
|
-
1) Install the latest release of `spin` from `PyPI <https://pypi.org/project/spin/>`_:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
pip install spin
|
|
30
|
-
```
|
|
31
|
-
-->
|
|
32
|
-
|
|
33
|
-
1. Install the latest development version:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
pip install git+https://github.com/gtca/spin.git@main
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Release notes
|
|
40
|
-
|
|
41
|
-
See the [changelog][changelog].
|
|
42
|
-
|
|
43
|
-
## Contact
|
|
44
|
-
|
|
45
|
-
For questions and help requests, you can reach out in the [scverse discourse][scverse-discourse].
|
|
46
|
-
If you found a bug, please use the [issue tracker][issue-tracker].
|
|
47
|
-
|
|
48
|
-
## Citation
|
|
49
|
-
|
|
50
|
-
> t.b.a
|
|
51
|
-
|
|
52
|
-
[scverse-discourse]: https://discourse.scverse.org/
|
|
53
|
-
[issue-tracker]: https://github.com/gtca/spin/issues
|
|
54
|
-
[changelog]: https://spin.readthedocs.io/latest/changelog.html
|
|
55
|
-
[link-docs]: https://spin.readthedocs.io
|
|
56
|
-
[link-api]: https://spin.readthedocs.io/latest/api.html
|
spin-0.0.1.dev0/pyproject.toml
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
build-backend = "flit_core.buildapi"
|
|
3
|
-
requires = [
|
|
4
|
-
"flit_core >=3.4,<4",
|
|
5
|
-
"importlib_metadata>=0.7; python_version < '3.8'",
|
|
6
|
-
]
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
[project]
|
|
10
|
-
name = "spin"
|
|
11
|
-
description = "Spatial omics analysis"
|
|
12
|
-
authors = [
|
|
13
|
-
{name = "Danila Bredikhin"},
|
|
14
|
-
]
|
|
15
|
-
maintainers = [
|
|
16
|
-
{name = "Danila Bredikhin", email = "danila.bredikhin@embl.de"},
|
|
17
|
-
]
|
|
18
|
-
urls.Documentation = "https://spin.readthedocs.io/"
|
|
19
|
-
urls.Source = "https://github.com/gtca/spin"
|
|
20
|
-
urls.Home-page = "https://github.com/gtca/spin"
|
|
21
|
-
version = "0.0.1.dev0"
|
|
22
|
-
requires-python = ">=3.8"
|
|
23
|
-
license = {file = "LICENSE"}
|
|
24
|
-
readme = "README.md"
|
|
25
|
-
dependencies = ["anndata"]
|
|
26
|
-
|
|
27
|
-
[project.optional-dependencies]
|
|
28
|
-
dev = [
|
|
29
|
-
# dev version generation
|
|
30
|
-
"bump2version",
|
|
31
|
-
]
|
|
32
|
-
doc = [
|
|
33
|
-
"sphinx>=4",
|
|
34
|
-
"furo",
|
|
35
|
-
"myst-parser",
|
|
36
|
-
"sphinxcontrib-bibtex>=1.0.0",
|
|
37
|
-
"scanpydoc[typehints]>=0.7.4",
|
|
38
|
-
# For notebooks
|
|
39
|
-
"nbsphinx",
|
|
40
|
-
"ipython>=8.3.0"
|
|
41
|
-
]
|
|
42
|
-
test = [
|
|
43
|
-
"pytest",
|
|
44
|
-
"pytest-cov",
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
[tool.coverage.run]
|
|
48
|
-
source = ["spin"]
|
|
49
|
-
omit = [
|
|
50
|
-
"**/test_*.py",
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
[tool.pytest.ini_options]
|
|
54
|
-
testpaths = ["tests"]
|
|
55
|
-
xfail_strict = true
|
|
56
|
-
addopts = [
|
|
57
|
-
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
|
|
58
|
-
"--import-mode=importlib", # allow using test files with same name
|
|
59
|
-
]
|
|
60
|
-
filterwarnings = [
|
|
61
|
-
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
|
|
62
|
-
]
|
|
63
|
-
|
|
64
|
-
[tool.isort]
|
|
65
|
-
include_trailing_comma = true
|
|
66
|
-
multi_line_output = 3
|
|
67
|
-
profile = "black"
|
|
68
|
-
skip_glob = ["docs/*"]
|
|
69
|
-
|
|
70
|
-
[tool.black]
|
|
71
|
-
line-length = 120
|
|
72
|
-
target-version = ['py38']
|
|
73
|
-
include = '\.pyi?$'
|
|
74
|
-
exclude = '''
|
|
75
|
-
(
|
|
76
|
-
/(
|
|
77
|
-
\.eggs
|
|
78
|
-
| \.git
|
|
79
|
-
| \.hg
|
|
80
|
-
| \.mypy_cache
|
|
81
|
-
| \.tox
|
|
82
|
-
| \.venv
|
|
83
|
-
| _build
|
|
84
|
-
| buck-out
|
|
85
|
-
| build
|
|
86
|
-
| dist
|
|
87
|
-
)/
|
|
88
|
-
)
|
|
89
|
-
'''
|
|
90
|
-
|
|
91
|
-
[tool.jupytext]
|
|
92
|
-
formats = "ipynb,md"
|
spin-0.0.1.dev0/spin/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .basic import basic_plot
|
spin-0.0.1.dev0/spin/pl/basic.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .basic import basic_preproc
|
spin-0.0.1.dev0/spin/pp/basic.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .basic import basic_tool
|