├── .all-contributorsrc
├── .circleci
└── config.yml
├── .docs
├── Makefile
├── apidoc.sh
├── changelog.md
├── conf.py
├── index.rst
├── requirements.txt
└── source
│ ├── modules.rst
│ ├── spython.client.rst
│ ├── spython.image.cmd.rst
│ ├── spython.image.rst
│ ├── spython.instance.cmd.rst
│ ├── spython.instance.rst
│ ├── spython.logger.rst
│ ├── spython.main.base.rst
│ ├── spython.main.parse.rst
│ ├── spython.main.rst
│ ├── spython.rst
│ ├── spython.tests.rst
│ └── spython.utils.rst
├── .github
├── CODE_OF_CONDUCT.md
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── question.md
├── dev-requirements.txt
├── stale.yml
└── workflows
│ ├── main.yml
│ ├── release.yaml
│ └── update-contributors.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .pylintrc
├── .tributors
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── Singularity
├── docs
├── README.md
├── _config.yml
├── _data
│ └── toc.yml
├── _includes
│ ├── editable.html
│ ├── head.html
│ ├── navigation.html
│ ├── page-footer.html
│ ├── page-header.html
│ ├── search.html
│ └── toc.html
├── _layouts
│ ├── default.html
│ └── page.html
├── api
│ ├── _sources
│ │ ├── changelog.md.txt
│ │ ├── index.rst.txt
│ │ └── source
│ │ │ ├── modules.rst.txt
│ │ │ ├── spython.client.rst.txt
│ │ │ ├── spython.image.cmd.rst.txt
│ │ │ ├── spython.image.rst.txt
│ │ │ ├── spython.instance.cmd.rst.txt
│ │ │ ├── spython.instance.rst.txt
│ │ │ ├── spython.logger.rst.txt
│ │ │ ├── spython.main.base.rst.txt
│ │ │ ├── spython.main.parse.rst.txt
│ │ │ ├── spython.main.rst.txt
│ │ │ ├── spython.rst.txt
│ │ │ ├── spython.tests.rst.txt
│ │ │ └── spython.utils.rst.txt
│ ├── assets
│ │ ├── basic.css
│ │ ├── css
│ │ │ ├── badge_only.css
│ │ │ ├── fonts
│ │ │ │ ├── Roboto-Slab-Bold.woff
│ │ │ │ ├── Roboto-Slab-Bold.woff2
│ │ │ │ ├── Roboto-Slab-Regular.woff
│ │ │ │ ├── Roboto-Slab-Regular.woff2
│ │ │ │ ├── fontawesome-webfont.eot
│ │ │ │ ├── fontawesome-webfont.svg
│ │ │ │ ├── fontawesome-webfont.ttf
│ │ │ │ ├── fontawesome-webfont.woff
│ │ │ │ ├── fontawesome-webfont.woff2
│ │ │ │ ├── lato-bold-italic.woff
│ │ │ │ ├── lato-bold-italic.woff2
│ │ │ │ ├── lato-bold.woff
│ │ │ │ ├── lato-bold.woff2
│ │ │ │ ├── lato-normal-italic.woff
│ │ │ │ ├── lato-normal-italic.woff2
│ │ │ │ ├── lato-normal.woff
│ │ │ │ └── lato-normal.woff2
│ │ │ └── theme.css
│ │ ├── doctools.js
│ │ ├── documentation_options.js
│ │ ├── file.png
│ │ ├── jquery-3.5.1.js
│ │ ├── jquery.js
│ │ ├── js
│ │ │ ├── badge_only.js
│ │ │ ├── html5shiv-printshiv.min.js
│ │ │ ├── html5shiv.min.js
│ │ │ └── theme.js
│ │ ├── language_data.js
│ │ ├── minus.png
│ │ ├── plus.png
│ │ ├── pygments.css
│ │ ├── searchtools.js
│ │ ├── underscore-1.13.1.js
│ │ └── underscore.js
│ ├── changelog.html
│ ├── genindex.html
│ ├── index.html
│ ├── modules
│ │ ├── index.html
│ │ └── spython
│ │ │ ├── client.html
│ │ │ ├── client
│ │ │ ├── recipe.html
│ │ │ ├── shell.html
│ │ │ └── test.html
│ │ │ ├── image.html
│ │ │ ├── instance.html
│ │ │ ├── instance
│ │ │ ├── cmd.html
│ │ │ └── cmd
│ │ │ │ ├── start.html
│ │ │ │ └── stop.html
│ │ │ ├── logger
│ │ │ ├── message.html
│ │ │ ├── progress.html
│ │ │ └── spinner.html
│ │ │ ├── main.html
│ │ │ ├── main
│ │ │ ├── apps.html
│ │ │ ├── base.html
│ │ │ ├── base
│ │ │ │ ├── command.html
│ │ │ │ ├── flags.html
│ │ │ │ ├── generate.html
│ │ │ │ ├── logger.html
│ │ │ │ └── sutils.html
│ │ │ ├── build.html
│ │ │ ├── execute.html
│ │ │ ├── help.html
│ │ │ ├── inspect.html
│ │ │ ├── instances.html
│ │ │ ├── parse
│ │ │ │ └── recipe.html
│ │ │ ├── pull.html
│ │ │ └── run.html
│ │ │ └── utils
│ │ │ ├── fileio.html
│ │ │ └── terminal.html
│ ├── objects.inv
│ ├── py-modindex.html
│ ├── search.html
│ ├── searchindex.js
│ └── source
│ │ ├── modules.html
│ │ ├── spython.client.html
│ │ ├── spython.html
│ │ ├── spython.image.cmd.html
│ │ ├── spython.image.html
│ │ ├── spython.instance.cmd.html
│ │ ├── spython.instance.html
│ │ ├── spython.logger.html
│ │ ├── spython.main.base.html
│ │ ├── spython.main.html
│ │ ├── spython.main.parse.html
│ │ ├── spython.tests.html
│ │ └── spython.utils.html
├── css
│ ├── normalize.css
│ └── sregistry.css
├── favicon.ico
├── img
│ ├── logo.png
│ ├── robot.png
│ ├── shub-logo.png
│ └── stanford.png
├── js
│ ├── jquery.dlmenu.js
│ ├── lunr.min.js
│ ├── modernizr.custom.js
│ ├── search.js
│ └── toc.js
└── pages
│ ├── client.md
│ ├── commands-images.md
│ ├── commands-instances.md
│ ├── commands-oci.md
│ ├── commands.md
│ ├── contribute-docs.md
│ ├── install.md
│ ├── recipes.md
│ └── search.html
├── pyproject.toml
├── setup.cfg
├── setup.py
└── spython
├── README.md
├── __init__.py
├── client
├── __init__.py
├── recipe.py
├── shell.py
└── test.py
├── image.py
├── instance
├── __init__.py
└── cmd
│ ├── __init__.py
│ ├── logs.py
│ ├── start.py
│ └── stop.py
├── logger
├── __init__.py
├── compatibility.py
├── message.py
├── progress.py
└── spinner.py
├── main
├── __init__.py
├── apps.py
├── base
│ ├── Dockerfile
│ ├── README.md
│ ├── __init__.py
│ ├── command.py
│ ├── flags.py
│ ├── generate.py
│ ├── logger.py
│ └── sutils.py
├── build.py
├── execute.py
├── export.py
├── help.py
├── inspect.py
├── instances.py
├── parse
│ ├── __init__.py
│ ├── parsers
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── docker.py
│ │ └── singularity.py
│ ├── recipe.py
│ └── writers
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── docker.py
│ │ └── singularity.py
├── pull.py
└── run.py
├── oci
├── README.md
├── __init__.py
├── cmd
│ ├── __init__.py
│ ├── actions.py
│ ├── mounts.py
│ └── states.py
└── config.json
├── tests
├── Xtest_oci.py
├── __init__.py
├── conftest.py
├── helpers.sh
├── test_base.py
├── test_client.py
├── test_client.sh
├── test_conversion.py
├── test_instances.py
├── test_parsers.py
├── test_recipe.py
├── test_utils.py
├── test_writers.py
└── testdata
│ ├── Dockerfile
│ ├── README.md
│ ├── Singularity
│ ├── docker2singularity
│ ├── add.def
│ ├── add.docker
│ ├── argsub.def
│ ├── argsub.docker
│ ├── cmd.def
│ ├── cmd.docker
│ ├── comments.def
│ ├── comments.docker
│ ├── copy.def
│ ├── copy.docker
│ ├── entrypoint-cmd.def
│ ├── entrypoint-cmd.docker
│ ├── entrypoint.def
│ ├── entrypoint.docker
│ ├── expose.def
│ ├── expose.docker
│ ├── from.def
│ ├── from.docker
│ ├── healthcheck.def
│ ├── healthcheck.docker
│ ├── label.def
│ ├── label.docker
│ ├── multiple-lines.def
│ ├── multiple-lines.docker
│ ├── multistage.def
│ ├── multistage.docker
│ ├── user.def
│ ├── user.docker
│ ├── workdir.def
│ └── workdir.docker
│ └── singularity2docker
│ ├── files.def
│ ├── files.docker
│ ├── from.def
│ ├── from.docker
│ ├── labels.def
│ ├── labels.docker
│ ├── multiple-lines.def
│ ├── multiple-lines.docker
│ ├── multistage.def
│ ├── multistage.docker
│ ├── post.def
│ ├── post.docker
│ ├── runscript.def
│ ├── runscript.docker
│ ├── test.def
│ └── test.docker
├── utils
├── __init__.py
├── fileio.py
├── misc.py
└── terminal.py
└── version.py
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | # Modified from hpcng/singularity
4 |
5 | workflows:
6 | version: 2
7 | build_and_test:
8 | jobs:
9 | - test_singularity_python:
10 | filters:
11 | branches:
12 | ignore: master
13 |
14 | setup_environment: &setup_environment
15 | name: Setup environment
16 | command: |-
17 | echo 'set -x' >> $BASH_ENV
18 | echo 'export GOPATH=$HOME/go' >> $BASH_ENV
19 | echo 'export GOROOT=/usr/local/go' >> $BASH_ENV
20 | echo 'export GOBIN=$HOME/go/bin' >> $BASH_ENV
21 | echo 'export PATH=$GOBIN:$GOROOT/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin' >> $BASH_ENV
22 | env | sed -e 's,^,ENV: ,' | sort
23 | test -e $BASH_ENV && sed -e 's,^,BASH_ENV: ,' < $BASH_ENV
24 |
25 | update_go: &update_go
26 | name: Update Go to 1.14.9
27 | working_directory: /tmp
28 | command: |-
29 | wget https://dl.google.com/go/go1.14.9.linux-amd64.tar.gz
30 | sudo rm -rf $GOROOT
31 | sudo tar -C /usr/local -xzf go1.14.9.linux-amd64.tar.gz
32 | sudo ln -s $GOROOT/bin/go /usr/local/bin/go
33 |
34 | fetch_deb_deps: &fetch_deb_deps
35 | name: Fetch deps
36 | working_directory: /tmp
37 | command: |-
38 | # https://discuss.circleci.com/t/could-not-get-lock-var-lib-apt-lists-lock/28337/4
39 | sudo killall -9 apt-get || true && \
40 | sudo apt-get update -y && \
41 | sudo apt-get install -y build-essential squashfs-tools libseccomp-dev cryptsetup
42 |
43 | build_singularity: &install_singularity
44 | name: Build Singularity
45 | working_directory: ~/go/singularity
46 | command: |-
47 | cd ~/go
48 | wget https://github.com/hpcng/singularity/releases/download/v3.7.1/singularity-3.7.1.tar.gz && \
49 | tar -xzvf singularity-3.7.1.tar.gz && \
50 | cd singularity
51 | ./mconfig -p /usr/local && \
52 | make -C builddir && \
53 | sudo make -C builddir install
54 |
55 | install_spython: &install_spython
56 | name: install spython
57 | command: |-
58 | export PATH=~/conda/Python3/bin:$PATH
59 | which python
60 | pip uninstall spython --yes || echo "Not installed"
61 | python --version
62 | python setup.py install
63 |
64 | install_dependencies: &install_dependencies
65 | name: install dependencies
66 | command: |-
67 | PYTHON_VERSION=3
68 | CONDA_PATH="$HOME/conda/Python${PYTHON_VERSION}"
69 | echo 'export PATH="'"$CONDA_PATH"'/bin:$PATH"' >> "$BASH_ENV"
70 | source "$BASH_ENV"
71 | if [ ! -d "$CONDA_PATH" ]; then
72 | CONDA_SCRIPT=Miniconda${PYTHON_VERSION}-latest-Linux-x86_64.sh
73 | wget https://repo.anaconda.com/miniconda/$CONDA_SCRIPT
74 | /bin/bash $CONDA_SCRIPT -b -p $CONDA_PATH
75 | else
76 | echo "Miniconda is already installed, continuing to build."
77 | fi
78 | python --version
79 | [ $(python -c'import sys;print(sys.version_info.major)') -eq $PYTHON_VERSION ]
80 |
81 | pip install --upgrade pytest
82 | pip install black || true
83 |
84 | run_linter: &run_linter
85 | name: run linter
86 | command: |-
87 | export PATH=~/conda/Python3/bin:$PATH
88 | cd ~/repo
89 | black --check spython
90 |
91 | test_spython: &test_spython
92 | name: Test Singularity Python
93 | command: |-
94 | export PATH=~/conda/Python3/bin:$PATH
95 | pytest ~/repo/spython
96 |
97 |
98 | jobs:
99 | test_singularity_python:
100 | working_directory: ~/repo
101 | machine:
102 | image: ubuntu-2004:202008-01
103 | steps:
104 | - checkout
105 | - restore_cache:
106 | keys: v2-dependencies
107 | - run: *install_dependencies
108 | - run: *setup_environment
109 | - run: *update_go
110 | - run: *fetch_deb_deps
111 | - run: *install_singularity
112 | - run: *install_spython
113 | - save_cache:
114 | paths:
115 | - ~/conda
116 | key: v3-dependencies
117 | - run: *run_linter
118 | - run: *test_spython
119 |
--------------------------------------------------------------------------------
/.docs/apidoc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # If the modules changed, the content of "source" should be backed up and
3 | # new files generated (to update) by doing:
4 | #
5 | # sphinx-apidoc -o source/ ../spython
6 |
7 | HERE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
8 | BASE=$(dirname $HERE)
9 | cd $HERE
10 | cd $BASE && python setup.py install && cd $HERE && make html
11 | rm -rf $BASE/docs/api
12 |
13 | # Create new folders
14 | mkdir -p $BASE/docs/api
15 |
16 | # Rename folders
17 | find $HERE/_build/html -exec sed -i -e 's/_static/assets/g' {} \;
18 | find $HERE/_build/html -exec sed -i -e 's/_modules/modules/g' {} \;
19 |
20 | # Copy to new locations
21 | mv $HERE/_build/html/_static $BASE/docs/api/assets
22 | mv $HERE/_build/html/_modules $BASE/docs/api/modules
23 | cp -R $HERE/_build/html/* $BASE/docs/api
24 |
--------------------------------------------------------------------------------
/.docs/changelog.md:
--------------------------------------------------------------------------------
1 | ../CHANGELOG.md
--------------------------------------------------------------------------------
/.docs/index.rst:
--------------------------------------------------------------------------------
1 | .. Singularity Python API documentation master file, created by
2 | sphinx-quickstart on Sun Feb 11 12:17:05 2018.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to Singularity Python API's documentation!
7 | ==================================================
8 |
9 | Singularity Python is the Python API for working with Singularity containers. The module is licensed
10 | under the MPL 2.0 LICENSE.
11 |
12 | Contents:
13 |
14 | .. toctree::
15 | :maxdepth: 3
16 |
17 | source/spython.rst
18 | changelog.md
19 |
20 |
21 | Indices and tables
22 | ------------------
23 |
24 | * :ref:`modindex`
25 |
--------------------------------------------------------------------------------
/.docs/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx
2 | sphinxcontrib-napoleon
3 | sphinx-argparse
4 | sphinx_rtd_theme
5 | docutils==0.12
6 | recommonmark
7 | configargparse
8 | appdirs
9 |
--------------------------------------------------------------------------------
/.docs/source/modules.rst:
--------------------------------------------------------------------------------
1 | spython
2 | =======
3 |
4 | .. toctree::
5 | :maxdepth: 4
6 |
7 | spython
8 |
--------------------------------------------------------------------------------
/.docs/source/spython.client.rst:
--------------------------------------------------------------------------------
1 | spython\.client package
2 | =======================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.client\.recipe module
8 | ------------------------------
9 |
10 | .. automodule:: spython.client.recipe
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.client\.shell module
16 | -----------------------------
17 |
18 | .. automodule:: spython.client.shell
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.client\.test module
24 | ----------------------------
25 |
26 | .. automodule:: spython.client.test
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: spython.client
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/.docs/source/spython.image.cmd.rst:
--------------------------------------------------------------------------------
1 | spython\.image\.cmd package
2 | ===========================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.image\.cmd\.create module
8 | ----------------------------------
9 |
10 | .. automodule:: spython.image.cmd.create
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.image\.cmd\.export module
16 | ----------------------------------
17 |
18 | .. automodule:: spython.image.cmd.export
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.image\.cmd\.importcmd module
24 | -------------------------------------
25 |
26 | .. automodule:: spython.image.cmd.importcmd
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | spython\.image\.cmd\.utils module
32 | ---------------------------------
33 |
34 | .. automodule:: spython.image.cmd.utils
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 |
40 | Module contents
41 | ---------------
42 |
43 | .. automodule:: spython.image.cmd
44 | :members:
45 | :undoc-members:
46 | :show-inheritance:
47 |
--------------------------------------------------------------------------------
/.docs/source/spython.image.rst:
--------------------------------------------------------------------------------
1 | spython\.image package
2 | ======================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | spython.image.cmd
10 |
11 | Module contents
12 | ---------------
13 |
14 | .. automodule:: spython.image
15 | :members:
16 | :undoc-members:
17 | :show-inheritance:
18 |
--------------------------------------------------------------------------------
/.docs/source/spython.instance.cmd.rst:
--------------------------------------------------------------------------------
1 | spython\.instance\.cmd package
2 | ==============================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.instance\.cmd\.iutils module
8 | -------------------------------------
9 |
10 | .. automodule:: spython.instance.cmd.iutils
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.instance\.cmd\.start module
16 | ------------------------------------
17 |
18 | .. automodule:: spython.instance.cmd.start
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.instance\.cmd\.stop module
24 | -----------------------------------
25 |
26 | .. automodule:: spython.instance.cmd.stop
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: spython.instance.cmd
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/.docs/source/spython.instance.rst:
--------------------------------------------------------------------------------
1 | spython\.instance package
2 | =========================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | spython.instance.cmd
10 |
11 | Module contents
12 | ---------------
13 |
14 | .. automodule:: spython.instance
15 | :members:
16 | :undoc-members:
17 | :show-inheritance:
18 |
--------------------------------------------------------------------------------
/.docs/source/spython.logger.rst:
--------------------------------------------------------------------------------
1 | spython\.logger package
2 | =======================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.logger\.message module
8 | -------------------------------
9 |
10 | .. automodule:: spython.logger.message
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.logger\.progress module
16 | --------------------------------
17 |
18 | .. automodule:: spython.logger.progress
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.logger\.spinner module
24 | -------------------------------
25 |
26 | .. automodule:: spython.logger.spinner
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: spython.logger
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/.docs/source/spython.main.base.rst:
--------------------------------------------------------------------------------
1 | spython\.main\.base package
2 | ===========================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.main\.base\.command module
8 | -----------------------------------
9 |
10 | .. automodule:: spython.main.base.command
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.main\.base\.flags module
16 | ---------------------------------
17 |
18 | .. automodule:: spython.main.base.flags
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.main\.base\.generate module
24 | ------------------------------------
25 |
26 | .. automodule:: spython.main.base.generate
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | spython\.main\.base\.logger module
32 | ----------------------------------
33 |
34 | .. automodule:: spython.main.base.logger
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | spython\.main\.base\.sutils module
40 | ----------------------------------
41 |
42 | .. automodule:: spython.main.base.sutils
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 |
48 | Module contents
49 | ---------------
50 |
51 | .. automodule:: spython.main.base
52 | :members:
53 | :undoc-members:
54 | :show-inheritance:
55 |
--------------------------------------------------------------------------------
/.docs/source/spython.main.parse.rst:
--------------------------------------------------------------------------------
1 | spython\.main\.parse package
2 | ============================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.main\.parse\.converters module
8 | ---------------------------------------
9 |
10 | .. automodule:: spython.main.parse.converters
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.main\.parse\.docker module
16 | -----------------------------------
17 |
18 | .. automodule:: spython.main.parse.docker
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.main\.parse\.environment module
24 | ----------------------------------------
25 |
26 | .. automodule:: spython.main.parse.environment
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | spython\.main\.parse\.recipe module
32 | -----------------------------------
33 |
34 | .. automodule:: spython.main.parse.recipe
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | spython\.main\.parse\.singularity module
40 | ----------------------------------------
41 |
42 | .. automodule:: spython.main.parse.singularity
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 |
48 | Module contents
49 | ---------------
50 |
51 | .. automodule:: spython.main.parse
52 | :members:
53 | :undoc-members:
54 | :show-inheritance:
55 |
--------------------------------------------------------------------------------
/.docs/source/spython.main.rst:
--------------------------------------------------------------------------------
1 | spython\.main package
2 | =====================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | spython.main.base
10 | spython.main.parse
11 |
12 | Submodules
13 | ----------
14 |
15 | spython\.main\.apps module
16 | --------------------------
17 |
18 | .. automodule:: spython.main.apps
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.main\.build module
24 | ---------------------------
25 |
26 | .. automodule:: spython.main.build
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | spython\.main\.execute module
32 | -----------------------------
33 |
34 | .. automodule:: spython.main.execute
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | spython\.main\.help module
40 | --------------------------
41 |
42 | .. automodule:: spython.main.help
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 | spython\.main\.inspect module
48 | -----------------------------
49 |
50 | .. automodule:: spython.main.inspect
51 | :members:
52 | :undoc-members:
53 | :show-inheritance:
54 |
55 | spython\.main\.instances module
56 | -------------------------------
57 |
58 | .. automodule:: spython.main.instances
59 | :members:
60 | :undoc-members:
61 | :show-inheritance:
62 |
63 | spython\.main\.pull module
64 | --------------------------
65 |
66 | .. automodule:: spython.main.pull
67 | :members:
68 | :undoc-members:
69 | :show-inheritance:
70 |
71 | spython\.main\.run module
72 | -------------------------
73 |
74 | .. automodule:: spython.main.run
75 | :members:
76 | :undoc-members:
77 | :show-inheritance:
78 |
79 |
80 | Module contents
81 | ---------------
82 |
83 | .. automodule:: spython.main
84 | :members:
85 | :undoc-members:
86 | :show-inheritance:
87 |
--------------------------------------------------------------------------------
/.docs/source/spython.rst:
--------------------------------------------------------------------------------
1 | spython package
2 | ===============
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | spython.client
10 | spython.image
11 | spython.instance
12 | spython.logger
13 | spython.main
14 | spython.utils
15 |
16 | Submodules
17 | ----------
18 |
19 | spython\.version module
20 | -----------------------
21 |
22 | .. automodule:: spython.version
23 | :members:
24 | :undoc-members:
25 | :show-inheritance:
26 |
27 |
28 | Module contents
29 | ---------------
30 |
31 | .. automodule:: spython
32 | :members:
33 | :undoc-members:
34 | :show-inheritance:
35 |
--------------------------------------------------------------------------------
/.docs/source/spython.tests.rst:
--------------------------------------------------------------------------------
1 | spython\.tests package
2 | ======================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.tests\.test\_client module
8 | -----------------------------------
9 |
10 | .. automodule:: spython.tests.test_client
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.tests\.test\_instances module
16 | --------------------------------------
17 |
18 | .. automodule:: spython.tests.test_instances
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.tests\.test\_utils module
24 | ----------------------------------
25 |
26 | .. automodule:: spython.tests.test_utils
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: spython.tests
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/.docs/source/spython.utils.rst:
--------------------------------------------------------------------------------
1 | spython\.utils package
2 | ======================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.utils\.fileio module
8 | -----------------------------
9 |
10 | .. automodule:: spython.utils.fileio
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.utils\.terminal module
16 | -------------------------------
17 |
18 | .. automodule:: spython.utils.terminal
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 |
24 | Module contents
25 | ---------------
26 |
27 | .. automodule:: spython.utils
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team leader @vsoch. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: vsoch
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve singularity python
4 |
5 | ---
6 |
7 | ## Expected Behavior
8 |
9 | [ Please describe the behavior that are expecting ]
10 |
11 | ## Actual Behavior
12 |
13 | ## Steps to Reproduce
14 |
15 | [ Please provide detailed steps for reproducing the issue and reproducible Code Snippet ]
16 |
17 | ## Context
18 |
19 | [provide more detailed introduction to the issue itself . This is for make a reproducible issue.]
20 | * Operating System:
21 | * singularity version:
22 | * spython version:
23 | * python version:
24 |
25 | ## Failure Logs
26 |
27 | [ log or files here. If relevant, include a screenshot]
28 |
29 | ## Possible Fix
30 |
31 | [ the suggest fixes or reasons for the bug]
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for singularity python
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: Ask a question, or prompt an update of the docs
4 |
5 | ---
6 |
7 | ## What is your question?
8 |
9 | [write your question here, and answer the prompts below]
10 |
11 | - [ ] I found the documentation at https://singularityhub.github.io/singularity-cli/
12 | - [ ] The documentation does not answer my question
13 |
14 | If the documentation doesn't answer your question, where do you think it would be appropriate to add (i.e., where did you go looking for it?)
15 |
--------------------------------------------------------------------------------
/.github/dev-requirements.txt:
--------------------------------------------------------------------------------
1 | pre-commit
2 | black==23.3.0
3 | isort
4 | flake8
5 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: wontfix
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: spython-ci
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request: []
8 |
9 | jobs:
10 | formatting:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 |
15 | - name: Check Spelling
16 | uses: crate-ci/typos@592b36d23c62cb378f6097a292bc902ee73f93ef # version 1.0.4
17 | with:
18 | files: ./spython ./.docs ./README.md
19 |
20 | - name: Setup black linter
21 | run: conda create --quiet --name black pyflakes
22 |
23 | - name: Lint and format Python code
24 | run: |
25 | export PATH="/usr/share/miniconda/bin:$PATH"
26 | source activate black
27 | pip install -r .github/dev-requirements.txt
28 | pre-commit run --all-files
29 |
30 | pytest:
31 | runs-on: ubuntu-latest
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | python-version: [3.7, 3.8, 3.9]
36 |
37 | steps:
38 | - uses: actions/checkout@v3
39 | - uses: singularityhub/install-singularity@main
40 | - name: Set up Python ${{ matrix.python-version }}
41 | uses: actions/setup-python@v4
42 | with:
43 | python-version: ${{ matrix.python-version }}
44 |
45 | - name: Install dependencies
46 | run: |
47 | python -m pip install --upgrade pip
48 | pip install pytest semver pytest-runner requests
49 |
50 | - name: Run unit tests
51 | run: pytest -xs spython/tests/test*.py
52 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release Python Package
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | deploy:
9 | runs-on: ubuntu-20.04
10 |
11 | steps:
12 | - uses: actions/checkout@v2
13 |
14 | - name: Install
15 | run: conda create --quiet --name spython twine
16 |
17 | - name: Install dependencies
18 | run: |
19 | export PATH="/usr/share/miniconda/bin:$PATH"
20 | source activate spython
21 | pip install -e .
22 | pip install setuptools wheel twine
23 | - name: Build and publish
24 | env:
25 | TWINE_USERNAME: ${{ secrets.PYPI_USER }}
26 | TWINE_PASSWORD: ${{ secrets.PYPI_PASS }}
27 | run: |
28 | export PATH="/usr/share/miniconda/bin:$PATH"
29 | source activate spython
30 | python setup.py sdist bdist_wheel
31 | twine upload dist/*
32 |
--------------------------------------------------------------------------------
/.github/workflows/update-contributors.yaml:
--------------------------------------------------------------------------------
1 | name: allcontributors-auto-detect
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | Update:
10 | name: Generate
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout Repository
14 | uses: actions/checkout@v4
15 | - name: Tributors Update
16 | uses: con/tributors@master
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 | with:
20 | parsers: unset
21 | update_lookup: github
22 | log_level: DEBUG
23 | force: true
24 | threshold: 1
25 | run_twice: true
26 |
27 | - name: Checkout New Branch
28 | env:
29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30 | BRANCH_AGAINST: "master"
31 | run: |
32 | printf "GitHub Actor: ${GITHUB_ACTOR}\n"
33 | export BRANCH_FROM="contributors/update-$(date '+%Y-%m-%d')"
34 | git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
35 |
36 | BRANCH_EXISTS=$(git ls-remote --heads origin ${BRANCH_FROM})
37 | if [[ -z ${BRANCH_EXISTS} ]]; then
38 | printf "Branch does not exist in remote.\n"
39 | else
40 | printf "Branch already exists in remote.\n"
41 | exit 1
42 | fi
43 | git branch
44 | git checkout -b "${BRANCH_FROM}" || git checkout "${BRANCH_FROM}"
45 | git branch
46 |
47 | git config --global user.name "github-actions"
48 | git config --global user.email "github-actions@users.noreply.github.com"
49 | git status
50 |
51 | if git diff-index --quiet HEAD --; then
52 | export OPEN_PULL_REQUEST=0
53 | printf "No changes\n"
54 | else
55 | export OPEN_PULL_REQUEST=1
56 | printf "Changes\n"
57 | git commit -a -m "Automated deployment to update contributors $(date '+%Y-%m-%d')"
58 | git push origin "${BRANCH_FROM}"
59 | fi
60 |
61 | echo "OPEN_PULL_REQUEST=${OPEN_PULL_REQUEST}" >> $GITHUB_ENV
62 | echo "PULL_REQUEST_FROM_BRANCH=${BRANCH_FROM}" >> $GITHUB_ENV
63 | echo "PULL_REQUEST_TITLE=[tributors] ${BRANCH_FROM}" >> $GITHUB_ENV
64 | echo "PULL_REQUEST_BODY=Tributors update automated pull request." >> $GITHUB_ENV
65 |
66 | - name: Open Pull Request
67 | uses: vsoch/pull-request-action@master
68 | if: ${{ env.OPEN_PULL_REQUEST == '1' }}
69 | env:
70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71 | PULL_REQUEST_BRANCH: "master"
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | spython.egg-info
2 | __pycache__
3 | *.img
4 | *.simg
5 | pypi.sh
6 | _site
7 | .eggs
8 | _build
9 | build
10 | dist
11 | *.pyc
12 | OLD
13 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | exclude: ".all-contributorsrc|.tributors"
2 | repos:
3 | - repo: https://github.com/pre-commit/pre-commit-hooks
4 | rev: v4.3.0
5 | hooks:
6 | - id: check-added-large-files
7 | - id: check-case-conflict
8 | - id: check-docstring-first
9 | - id: mixed-line-ending
10 |
11 | - repo: local
12 | hooks:
13 | - id: black
14 | name: black
15 | language: python
16 | types: [python]
17 | entry: black
18 |
19 | - id: isort
20 | name: isort
21 | args: [--filter-files]
22 | language: python
23 | types: [python]
24 | entry: isort
25 |
26 | - id: flake8
27 | name: flake8
28 | language: python
29 | types: [python]
30 | entry: flake8
31 |
--------------------------------------------------------------------------------
/.tributors:
--------------------------------------------------------------------------------
1 | {
2 | "vsoch": {
3 | "name": "Vanessasaurus",
4 | "bio": "I'm the Vanessasaurus!",
5 | "blog": "https://vsoch.github.io"
6 | },
7 | "Flamefire": {
8 | "name": "Alexander Grund",
9 | "blog": "https://github.com/Flamefire"
10 | },
11 | "pierlauro": {
12 | "name": "Pierlauro Sciarelli",
13 | "bio": "Computer Scientist",
14 | "blog": "https://github.com/pierlauro",
15 | "orcid": "0000-0002-3046-2869"
16 | },
17 | "al3x609": {
18 | "name": "Alex Bernal",
19 | "bio": "DevOps HPC Engineer -\r\nSuper computing laboratory - Industrial University of Santander.",
20 | "blog": "https://github.com/al3x609"
21 | },
22 | "wkpalan": {
23 | "name": "Kokulapalan (Gokul) Wimalanathan",
24 | "bio": "Genomics Data Scientist with experience in Bioinformatics, cloud computing, and HPC environments",
25 | "blog": "http://www.bioinformapping.com/",
26 | "orcid": "0000-0001-7811-935X",
27 | "affiliation": "University of Colombo Faculty of Science"
28 | },
29 | "tschoonj": {
30 | "name": "Tom Schoonjans",
31 | "email": "Tom.Schoonjans@genomicsplc.com",
32 | "bio": "I'm a software engineer working @genomicsplc, and am passionate about developing quality open source software.",
33 | "blog": "tschoonj.github.io",
34 | "orcid": "0000-0002-3919-3546",
35 | "affiliation": "Rosalind Franklin Institute"
36 | },
37 | "biosugar0": {
38 | "name": "Yuto Kimura",
39 | "email": "biosugar0@gmail.com",
40 | "bio": " SRE",
41 | "blog": "https://www.biosugar0.com/"
42 | },
43 | "kinow": {
44 | "name": "Bruno P. Kinoshita",
45 | "bio": "From Casa Verde, São Paulo . Now in CBD, Auckland .",
46 | "blog": "https://kinoshita.eti.br"
47 | },
48 | "cceyda": {
49 | "name": "Ceyda Cinarel",
50 | "bio": "AI researcher & engineer~\r\nall things NLP\r\n🤖 generative models\r\n★ like trying out new libraries & tools\r\n♥ Python ",
51 | "blog": "cceyda.github.io/blog/"
52 | },
53 | "shnizzedy": {
54 | "name": "Jon Clucas",
55 | "email": "jon.clucas@childmind.org",
56 | "bio": "Associate Software Developer in the Computational Neuroimaging Lab at the @ChildMindInstitute. they/them / he/him",
57 | "blog": "http://matter.childmind.org/jon-clucas.html",
58 | "orcid": "0000-0001-7590-5806",
59 | "affiliation": "Child Mind Institute"
60 | },
61 | "mobiusklein": {
62 | "name": "Joshua Klein",
63 | "email": "mobiusklein@gmail.com",
64 | "blog": "https://github.com/mobiusklein"
65 | },
66 | "MarDiehl": {
67 | "name": "Martin Diehl",
68 | "email": "martin.diehl@kuleuven.be",
69 | "bio": "Materials Science, Fortran, Python, DAMASK, open source",
70 | "blog": "https://martin-diehl.net"
71 | },
72 | "neumann-nico": {
73 | "name": "Nico Neumann",
74 | "bio": "Software Engineer",
75 | "blog": "https://www.linkedin.com/in/neumann-nico/",
76 | "orcid": "0000-0003-3094-6238"
77 | },
78 | "tcpan": {
79 | "name": "Tony Pan",
80 | "blog": "https://github.com/tcpan"
81 | },
82 | "tjgalvin": {
83 | "name": "tjgalvin",
84 | "blog": "https://github.com/tjgalvin"
85 | },
86 | "EricDeveaud": {
87 | "name": "Eric Deveaud",
88 | "blog": "http://www.pasteur.fr/ip/easysite/pasteur/fr/recherche/Centre-Informatique-pour-la-Biologie"
89 | },
90 | "pustoshilov-d": {
91 | "name": "Dmitry Pustoshilov",
92 | "email": "pustoshilov.d@gmail.com",
93 | "bio": "𝑳𝒊𝒇𝒆 & 𝑫𝒂𝒕𝒂 𝑺𝒄𝒊𝒆𝒏𝒕𝒊𝒔𝒕, open to work",
94 | "blog": "https://github.com/pustoshilov-d"
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md LICENSE
2 | recursive-include spython *
3 | recursive-exclude * __pycache__
4 | recursive-exclude * *.pyc
5 | recursive-exclude * *.pyo
6 | recursive-exclude * *.simg
7 | recursive-exclude * *.img
8 | prune docs
9 | prune .docs
10 |
--------------------------------------------------------------------------------
/Singularity:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: continuumio/miniconda3
3 |
4 | %runscript
5 | exec /opt/conda/bin/spython "$@"
6 |
7 | %labels
8 | maintainer vsochat@stanford.edu
9 |
10 | %post
11 | apt-get update && apt-get install -y git
12 |
13 | # Dependencies
14 | cd /opt
15 | git clone https://www.github.com/singularityhub/singularity-cli
16 | cd singularity-cli
17 | /opt/conda/bin/pip install setuptools
18 | /opt/conda/bin/python setup.py install
19 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: {{ site.name }}
4 | pdf: true
5 | permalink: /
6 | excluded_in_search: true
7 | ---
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | # Singularity Python
18 | Welcome to the Singularity Python documentation!
19 |
20 | ## What is Singularity Python
21 | Singularity Python is a Python API to work with the Singularity open source software. Are you a scientist or developer? You likely will be most interested in the ability to interact with Singularity containers from your own Python scripts. See here for the core module docstrings.
22 |
23 |
24 | ## Getting Started
25 |
26 | - [Installation](/singularity-cli/install): to your system with the python package manager pip, or use the client in a container that we provide.
27 | - [Recipes](/singularity-cli/recipes): Interact with and convert Dockerfile and Singularity Recipes
28 | - [Commands](/singularity-cli/commands): Learn how to interact with Singularity using functions from the spython module.
29 | - [Developers](https://singularityhub.github.io/singularity-cli/api/source/spython.main.html): The readthedocs documentation for the Singularity Python API.
30 |
31 |
32 | ## Getting Help
33 | This is an open source project. Please contribute to the package, or post feedback and questions as issues . You'll notice a little eliipsis ( ) next to each header section. If you click this, you can open an issue relevant to the section, grab a permalink, or suggest a change. You can also talk to us directly on Gitter.
34 |
35 | [](https://gitter.im/singularityhub/lobby)
36 |
37 |
38 | ## Contributing
39 | We welcome any kind of contribution, whether a suggestion, a bug fix, feature request, or new feature. See our [contributing docs](/singularity-cli/contribute-docs) for more information.
40 |
41 | ## License
42 | This code is licensed under the MPL 2.0 [LICENSE](https://github.com/singularityhub/singularity-cli/blob/master/LICENSE).
43 |
44 |
45 |
48 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | # Setup
2 | title: Singularity Python
3 | tagline: ""
4 | baseurl: "/singularity-cli"
5 | github: "https://www.github.com/singularityhub/singularity-cli"
6 |
7 | # About/contact
8 | author:
9 | name: Vanessa Sochat
10 | url: https://singularityhub.github.io/singularity-cli
11 |
12 | # Defaults
13 | defaults:
14 | -
15 | scope:
16 | path: ""
17 | type: "pages"
18 | values:
19 | layout: "default"
20 |
21 | #Others
22 | markdown: kramdown
23 |
--------------------------------------------------------------------------------
/docs/_data/toc.yml:
--------------------------------------------------------------------------------
1 | - title: "Home"
2 | url: "/singularity-cli/"
3 | slug: singularity-cli
4 | - title: "Installation"
5 | url: "/singularity-cli/install"
6 | slug: install
7 | - title: "Python API"
8 | url: "/singularity-cli/commands"
9 | slug: commands
10 | - title: "Image Commands"
11 | url: "/singularity-cli/commands-images"
12 | slug: images
13 | - title: "Instance Commands"
14 | url: "/singularity-cli/commands-instances"
15 | slug: instances
16 | - title: "OCI Commands"
17 | url: "/singularity-cli/commands-oci"
18 | slug: oci
19 | - title: "Recipe Generation"
20 | url: "/singularity-cli/recipes"
21 | slug: recipes
22 | - title: "Python API Docstring"
23 | url: "https://singularityhub.github.io/singularity-cli/api/source/spython.main.html"
24 | slug: docstring
25 |
--------------------------------------------------------------------------------
/docs/_includes/editable.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
34 |
66 |
--------------------------------------------------------------------------------
/docs/_includes/head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ site.title }}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/_includes/navigation.html:
--------------------------------------------------------------------------------
1 |
9 |
10 |
37 |
38 |
39 |
40 |
41 |
55 |
--------------------------------------------------------------------------------
/docs/_includes/page-footer.html:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/docs/_includes/page-header.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/_includes/search.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/docs/_includes/toc.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
--------------------------------------------------------------------------------
/docs/_layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% include head.html %}
5 | {% include navigation.html %}
6 |
7 |
8 | {% include page-header.html %}
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 | {% if page.toc %}
19 | {% include toc.html %}
20 | {% endif %}
21 | {{ content }}
22 | {% include editable.html %}
23 | {% include page-footer.html %}
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/_layouts/page.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 | {{ content }}
5 |
--------------------------------------------------------------------------------
/docs/api/_sources/index.rst.txt:
--------------------------------------------------------------------------------
1 | .. Singularity Python API documentation master file, created by
2 | sphinx-quickstart on Sun Feb 11 12:17:05 2018.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to Singularity Python API's documentation!
7 | ==================================================
8 |
9 | Singularity Python is the Python API for working with Singularity containers. The module is licensed
10 | under the MPL 2.0 LICENSE.
11 |
12 | Contents:
13 |
14 | .. toctree::
15 | :maxdepth: 3
16 |
17 | source/spython.rst
18 | changelog.md
19 |
20 |
21 | Indices and tables
22 | ------------------
23 |
24 | * :ref:`modindex`
25 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/modules.rst.txt:
--------------------------------------------------------------------------------
1 | spython
2 | =======
3 |
4 | .. toctree::
5 | :maxdepth: 4
6 |
7 | spython
8 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.client.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.client package
2 | =======================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.client\.recipe module
8 | ------------------------------
9 |
10 | .. automodule:: spython.client.recipe
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.client\.shell module
16 | -----------------------------
17 |
18 | .. automodule:: spython.client.shell
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.client\.test module
24 | ----------------------------
25 |
26 | .. automodule:: spython.client.test
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: spython.client
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.image.cmd.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.image\.cmd package
2 | ===========================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.image\.cmd\.create module
8 | ----------------------------------
9 |
10 | .. automodule:: spython.image.cmd.create
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.image\.cmd\.export module
16 | ----------------------------------
17 |
18 | .. automodule:: spython.image.cmd.export
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.image\.cmd\.importcmd module
24 | -------------------------------------
25 |
26 | .. automodule:: spython.image.cmd.importcmd
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | spython\.image\.cmd\.utils module
32 | ---------------------------------
33 |
34 | .. automodule:: spython.image.cmd.utils
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 |
40 | Module contents
41 | ---------------
42 |
43 | .. automodule:: spython.image.cmd
44 | :members:
45 | :undoc-members:
46 | :show-inheritance:
47 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.image.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.image package
2 | ======================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | spython.image.cmd
10 |
11 | Module contents
12 | ---------------
13 |
14 | .. automodule:: spython.image
15 | :members:
16 | :undoc-members:
17 | :show-inheritance:
18 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.instance.cmd.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.instance\.cmd package
2 | ==============================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.instance\.cmd\.iutils module
8 | -------------------------------------
9 |
10 | .. automodule:: spython.instance.cmd.iutils
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.instance\.cmd\.start module
16 | ------------------------------------
17 |
18 | .. automodule:: spython.instance.cmd.start
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.instance\.cmd\.stop module
24 | -----------------------------------
25 |
26 | .. automodule:: spython.instance.cmd.stop
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: spython.instance.cmd
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.instance.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.instance package
2 | =========================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | spython.instance.cmd
10 |
11 | Module contents
12 | ---------------
13 |
14 | .. automodule:: spython.instance
15 | :members:
16 | :undoc-members:
17 | :show-inheritance:
18 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.logger.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.logger package
2 | =======================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.logger\.message module
8 | -------------------------------
9 |
10 | .. automodule:: spython.logger.message
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.logger\.progress module
16 | --------------------------------
17 |
18 | .. automodule:: spython.logger.progress
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.logger\.spinner module
24 | -------------------------------
25 |
26 | .. automodule:: spython.logger.spinner
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: spython.logger
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.main.base.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.main\.base package
2 | ===========================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.main\.base\.command module
8 | -----------------------------------
9 |
10 | .. automodule:: spython.main.base.command
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.main\.base\.flags module
16 | ---------------------------------
17 |
18 | .. automodule:: spython.main.base.flags
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.main\.base\.generate module
24 | ------------------------------------
25 |
26 | .. automodule:: spython.main.base.generate
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | spython\.main\.base\.logger module
32 | ----------------------------------
33 |
34 | .. automodule:: spython.main.base.logger
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | spython\.main\.base\.sutils module
40 | ----------------------------------
41 |
42 | .. automodule:: spython.main.base.sutils
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 |
48 | Module contents
49 | ---------------
50 |
51 | .. automodule:: spython.main.base
52 | :members:
53 | :undoc-members:
54 | :show-inheritance:
55 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.main.parse.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.main\.parse package
2 | ============================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.main\.parse\.converters module
8 | ---------------------------------------
9 |
10 | .. automodule:: spython.main.parse.converters
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.main\.parse\.docker module
16 | -----------------------------------
17 |
18 | .. automodule:: spython.main.parse.docker
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.main\.parse\.environment module
24 | ----------------------------------------
25 |
26 | .. automodule:: spython.main.parse.environment
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | spython\.main\.parse\.recipe module
32 | -----------------------------------
33 |
34 | .. automodule:: spython.main.parse.recipe
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | spython\.main\.parse\.singularity module
40 | ----------------------------------------
41 |
42 | .. automodule:: spython.main.parse.singularity
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 |
48 | Module contents
49 | ---------------
50 |
51 | .. automodule:: spython.main.parse
52 | :members:
53 | :undoc-members:
54 | :show-inheritance:
55 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.main.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.main package
2 | =====================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | spython.main.base
10 | spython.main.parse
11 |
12 | Submodules
13 | ----------
14 |
15 | spython\.main\.apps module
16 | --------------------------
17 |
18 | .. automodule:: spython.main.apps
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.main\.build module
24 | ---------------------------
25 |
26 | .. automodule:: spython.main.build
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | spython\.main\.execute module
32 | -----------------------------
33 |
34 | .. automodule:: spython.main.execute
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | spython\.main\.help module
40 | --------------------------
41 |
42 | .. automodule:: spython.main.help
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 | spython\.main\.inspect module
48 | -----------------------------
49 |
50 | .. automodule:: spython.main.inspect
51 | :members:
52 | :undoc-members:
53 | :show-inheritance:
54 |
55 | spython\.main\.instances module
56 | -------------------------------
57 |
58 | .. automodule:: spython.main.instances
59 | :members:
60 | :undoc-members:
61 | :show-inheritance:
62 |
63 | spython\.main\.pull module
64 | --------------------------
65 |
66 | .. automodule:: spython.main.pull
67 | :members:
68 | :undoc-members:
69 | :show-inheritance:
70 |
71 | spython\.main\.run module
72 | -------------------------
73 |
74 | .. automodule:: spython.main.run
75 | :members:
76 | :undoc-members:
77 | :show-inheritance:
78 |
79 |
80 | Module contents
81 | ---------------
82 |
83 | .. automodule:: spython.main
84 | :members:
85 | :undoc-members:
86 | :show-inheritance:
87 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.rst.txt:
--------------------------------------------------------------------------------
1 | spython package
2 | ===============
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | spython.client
10 | spython.image
11 | spython.instance
12 | spython.logger
13 | spython.main
14 | spython.utils
15 |
16 | Submodules
17 | ----------
18 |
19 | spython\.version module
20 | -----------------------
21 |
22 | .. automodule:: spython.version
23 | :members:
24 | :undoc-members:
25 | :show-inheritance:
26 |
27 |
28 | Module contents
29 | ---------------
30 |
31 | .. automodule:: spython
32 | :members:
33 | :undoc-members:
34 | :show-inheritance:
35 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.tests.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.tests package
2 | ======================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.tests\.test\_client module
8 | -----------------------------------
9 |
10 | .. automodule:: spython.tests.test_client
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.tests\.test\_instances module
16 | --------------------------------------
17 |
18 | .. automodule:: spython.tests.test_instances
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | spython\.tests\.test\_utils module
24 | ----------------------------------
25 |
26 | .. automodule:: spython.tests.test_utils
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: spython.tests
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/docs/api/_sources/source/spython.utils.rst.txt:
--------------------------------------------------------------------------------
1 | spython\.utils package
2 | ======================
3 |
4 | Submodules
5 | ----------
6 |
7 | spython\.utils\.fileio module
8 | -----------------------------
9 |
10 | .. automodule:: spython.utils.fileio
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | spython\.utils\.terminal module
16 | -------------------------------
17 |
18 | .. automodule:: spython.utils.terminal
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 |
24 | Module contents
25 | ---------------
26 |
27 | .. automodule:: spython.utils
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
--------------------------------------------------------------------------------
/docs/api/assets/css/badge_only.css:
--------------------------------------------------------------------------------
1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
2 |
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/Roboto-Slab-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/Roboto-Slab-Bold.woff
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/Roboto-Slab-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/Roboto-Slab-Bold.woff2
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/Roboto-Slab-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/Roboto-Slab-Regular.woff
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/Roboto-Slab-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/Roboto-Slab-Regular.woff2
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/lato-bold-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/lato-bold-italic.woff
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/lato-bold-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/lato-bold-italic.woff2
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/lato-bold.woff
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/lato-normal-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/lato-normal-italic.woff
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/lato-normal-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/lato-normal-italic.woff2
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/lato-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/lato-normal.woff
--------------------------------------------------------------------------------
/docs/api/assets/css/fonts/lato-normal.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/css/fonts/lato-normal.woff2
--------------------------------------------------------------------------------
/docs/api/assets/documentation_options.js:
--------------------------------------------------------------------------------
1 | var DOCUMENTATION_OPTIONS = {
2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
3 | VERSION: '1',
4 | LANGUAGE: 'None',
5 | COLLAPSE_INDEX: false,
6 | BUILDER: 'html',
7 | FILE_SUFFIX: '.html',
8 | LINK_SUFFIX: '.html',
9 | HAS_SOURCE: true,
10 | SOURCELINK_SUFFIX: '.txt',
11 | NAVIGATION_WITH_KEYS: false,
12 | SHOW_SEARCH_SUMMARY: true,
13 | ENABLE_SEARCH_SHORTCUTS: true,
14 | };
15 |
--------------------------------------------------------------------------------
/docs/api/assets/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/file.png
--------------------------------------------------------------------------------
/docs/api/assets/js/badge_only.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
2 |
--------------------------------------------------------------------------------
/docs/api/assets/js/html5shiv-printshiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML=" ",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document);
5 |
--------------------------------------------------------------------------------
/docs/api/assets/js/html5shiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML=" ",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
5 |
--------------------------------------------------------------------------------
/docs/api/assets/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/minus.png
--------------------------------------------------------------------------------
/docs/api/assets/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/assets/plus.png
--------------------------------------------------------------------------------
/docs/api/objects.inv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/api/objects.inv
--------------------------------------------------------------------------------
/docs/api/search.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Search — Singularity Python API 1 documentation
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
49 |
50 |
51 |
52 |
53 | Singularity Python API
54 |
55 |
56 |
57 |
58 |
59 |
60 | »
61 | Search
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Please activate JavaScript to enable the search functionality.
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
99 |
100 |
101 |
102 |
103 |
108 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/img/logo.png
--------------------------------------------------------------------------------
/docs/img/robot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/img/robot.png
--------------------------------------------------------------------------------
/docs/img/shub-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/img/shub-logo.png
--------------------------------------------------------------------------------
/docs/img/stanford.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/docs/img/stanford.png
--------------------------------------------------------------------------------
/docs/js/search.js:
--------------------------------------------------------------------------------
1 | ---
2 | layout: null
3 | ---
4 | (function () {
5 | function getQueryVariable(variable) {
6 | var query = window.location.search.substring(1),
7 | vars = query.split("&");
8 |
9 | for (var i = 0; i < vars.length; i++) {
10 | var pair = vars[i].split("=");
11 |
12 | if (pair[0] === variable) {
13 | return decodeURIComponent(pair[1].replace(/\+/g, '%20')).trim();
14 | }
15 | }
16 | }
17 |
18 | function getPreview(query, content, previewLength) {
19 | previewLength = previewLength || (content.length * 2);
20 |
21 | var parts = query.split(" "),
22 | match = content.toLowerCase().indexOf(query.toLowerCase()),
23 | matchLength = query.length,
24 | preview;
25 |
26 | // Find a relevant location in content
27 | for (var i = 0; i < parts.length; i++) {
28 | if (match >= 0) {
29 | break;
30 | }
31 |
32 | match = content.toLowerCase().indexOf(parts[i].toLowerCase());
33 | matchLength = parts[i].length;
34 | }
35 |
36 | // Create preview
37 | if (match >= 0) {
38 | var start = match - (previewLength / 2),
39 | end = start > 0 ? match + matchLength + (previewLength / 2) : previewLength;
40 |
41 | preview = content.substring(start, end).trim();
42 |
43 | if (start > 0) {
44 | preview = "..." + preview;
45 | }
46 |
47 | if (end < content.length) {
48 | preview = preview + "...";
49 | }
50 |
51 | // Highlight query parts
52 | preview = preview.replace(new RegExp("(" + parts.join("|") + ")", "gi"), "$1 ");
53 | } else {
54 | // Use start of content if no match found
55 | preview = content.substring(0, previewLength).trim() + (content.length > previewLength ? "..." : "");
56 | }
57 |
58 | return preview;
59 | }
60 |
61 | function displaySearchResults(results, query) {
62 | var searchResultsEl = document.getElementById("search-results"),
63 | searchProcessEl = document.getElementById("search-process");
64 |
65 | if (results.length) {
66 | var resultsHTML = "";
67 | results.forEach(function (result) {
68 | var item = window.data[result.ref],
69 | contentPreview = getPreview(query, item.content, 170),
70 | titlePreview = getPreview(query, item.title);
71 |
72 | resultsHTML += "" + contentPreview + "
";
73 | });
74 |
75 | searchResultsEl.innerHTML = resultsHTML;
76 | searchProcessEl.innerText = "Showing";
77 | } else {
78 | searchResultsEl.style.display = "none";
79 | searchProcessEl.innerText = "No";
80 | }
81 | }
82 |
83 | window.index = lunr(function () {
84 | this.field("id");
85 | this.field("title", {boost: 10});
86 | this.field("categories");
87 | this.field("url");
88 | this.field("content");
89 | });
90 |
91 | var query = decodeURIComponent((getQueryVariable("q") || "").replace(/\+/g, "%20")),
92 | searchQueryContainerEl = document.getElementById("search-query-container"),
93 | searchQueryEl = document.getElementById("search-query");
94 |
95 | searchQueryEl.innerText = query;
96 | searchQueryContainerEl.style.display = "inline";
97 |
98 | for (var key in window.data) {
99 | window.index.add(window.data[key]);
100 | }
101 |
102 | displaySearchResults(window.index.search(query), query); // Hand the results off to be displayed
103 | })();
104 |
--------------------------------------------------------------------------------
/docs/js/toc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/ghiculescu/jekyll-table-of-contents
2 | (function($){
3 | $.fn.toc = function(options) {
4 | var defaults = {
5 | noBackToTopLinks: false,
6 | title: '',
7 | minimumHeaders: 3,
8 | headers: 'h1, h2, h3, h4',
9 | listType: 'ol', // values: [ol|ul]
10 | showEffect: 'show', // values: [show|slideDown|fadeIn|none]
11 | showSpeed: 'slow' // set to 0 to deactivate effect
12 | },
13 | settings = $.extend(defaults, options);
14 |
15 | var headers = $(settings.headers).filter(function() {
16 | // get all headers with an ID
17 | var previousSiblingName = $(this).prev().attr( "name" );
18 | if (!this.id && previousSiblingName) {
19 | this.id = $(this).attr( "id", previousSiblingName.replace(/\./g, "-") );
20 | }
21 | return this.id;
22 | }), output = $(this);
23 | if (!headers.length || headers.length < settings.minimumHeaders || !output.length) {
24 | return;
25 | }
26 |
27 | if (0 === settings.showSpeed) {
28 | settings.showEffect = 'none';
29 | }
30 |
31 | var render = {
32 | show: function() { output.hide().html(html).show(settings.showSpeed); },
33 | slideDown: function() { output.hide().html(html).slideDown(settings.showSpeed); },
34 | fadeIn: function() { output.hide().html(html).fadeIn(settings.showSpeed); },
35 | none: function() { output.html(html); }
36 | };
37 |
38 | var get_level = function(ele) { return parseInt(ele.nodeName.replace("H", ""), 10); }
39 | var highest_level = headers.map(function(_, ele) { return get_level(ele); }).get().sort()[0];
40 | var return_to_top = ' ';
41 |
42 | var level = get_level(headers[0]),
43 | this_level,
44 | html = settings.title + " <"+settings.listType+">";
45 | headers.on('click', function() {
46 | if (!settings.noBackToTopLinks) {
47 | window.location.hash = this.id;
48 | }
49 | })
50 | .addClass('clickable-header')
51 | .each(function(_, header) {
52 | this_level = get_level(header);
53 | if (!settings.noBackToTopLinks && this_level === highest_level) {
54 | $(header).addClass('top-level-header').after(return_to_top);
55 | }
56 | if (this_level === level) // same level as before; same indenting
57 | html += "" + header.innerHTML + " ";
58 | else if (this_level <= level){ // higher level than before; end parent ol
59 | for(i = this_level; i < level; i++) {
60 | html += " "+settings.listType+">"
61 | }
62 | html += "" + header.innerHTML + " ";
63 | }
64 | else if (this_level > level) { // lower level than before; expand the previous to contain a ol
65 | for(i = this_level; i > level; i--) {
66 | html += "<"+settings.listType+">"
67 | }
68 | html += "" + header.innerHTML + " ";
69 | }
70 | level = this_level; // update for the next one
71 | });
72 | html += ""+settings.listType+">";
73 | if (!settings.noBackToTopLinks) {
74 | $(document).on('click', '.back-to-top', function() {
75 | $(window).scrollTop(0);
76 | window.location.hash = '';
77 | });
78 | }
79 |
80 | render[settings.showEffect]();
81 | };
82 | })(jQuery);
83 |
--------------------------------------------------------------------------------
/docs/pages/client.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Command Line Client (spython)
4 | pdf: true
5 | permalink: /client
6 | toc: false
7 | ---
8 |
9 | # Singularity Python Command Line
10 | Welcome to the Singularity Python command line tool, `spython` documentation! Follow the links to the documentation that is of interest to you.
11 |
12 | - [Client](/singularity-cli/commands): Walk through the basic client commands to push, pull, build, and otherwise interact with images from Python
13 | - [Recipes](/singularity-cli/recipes): Convert and interact with Dockerfile and Singularity Recipes
14 |
15 |
16 |
20 |
--------------------------------------------------------------------------------
/docs/pages/commands.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Getting Started
4 | pdf: true
5 | permalink: /commands
6 | toc: false
7 | ---
8 |
9 | # Python API
10 |
11 | This commands section primarily focuses on using Singularity Python from within Python and then in an interactive shell (for testing). We will first discuss the Python API, meaning functions that you can use in python to work with Singularity images or instances. Python is strong in the world of scientific programming, and so if you are reading these notes it's likely that you want to integrate Singularity containers into your Python applications. We wrote you a client to do that! Please select the type of command you are interested in below to continue!
12 |
13 | - [Images](/singularity-cli/commands-images): base API to interact with containers:
14 | - [Instances](/singularity-cli/commands-instances): interact with container instances.
15 | - [OCI](/singularity-cli/commands-oci): interact with the OCI command group (3.1.0 and up)
16 |
17 | Note that for any command that allows specifying `sudo`, you can provide additional options to sudo
18 | (e.g., `-E` to preserve the environment) via `sudo_options`.
19 |
20 |
21 |
22 |
26 |
--------------------------------------------------------------------------------
/docs/pages/contribute-docs.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Contribute to Singularity Python Client Documentation
4 | pdf: true
5 | permalink: /contribute-docs
6 | toc: false
7 | ---
8 |
9 | # Singularity Python API Documentation
10 |
11 | The documentation is served in the Github pages site served in the docs folder.
12 | If you clone the repo, cd into this folder, and then install (and run) Jekyll you
13 | can usually get started working on the documentation.
14 |
15 |
16 | ## Dependencies
17 | Initially (on OS X), you will need to setup [Brew](http://brew.sh/) which is a package manager for OS X and [Git](https://git-scm.com/). To install Brew and Git, run the following commands:
18 |
19 | ```bash
20 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
21 | brew install git
22 | ```
23 | If you are on Debian/Ubuntu, then you can easily install git with `apt-get`
24 |
25 | ```bash
26 | apt-get update && apt-get install -y git
27 | ```
28 |
29 | ### Fork the Repository
30 | To contribute to the web based documentation, you should obtain a GitHub account and *fork* the Singularity Python repository by clicking the *fork* button on the top right of the page. Once forked, you will want to clone the fork of the repo to your computer. Let's say my Github username is *vsoch*:
31 |
32 | ```bash
33 | git clone https://github.com/vsoch/singularity-cli.git
34 | cd singularity-cli
35 | ```
36 |
37 | ### Install Jekyll
38 | You can also install Jekyll with brew.
39 |
40 | ```bash
41 | brew install ruby
42 | gem install jekyll
43 | gem install bundler
44 | bundle install
45 | ```
46 | On Ubuntu I do a different method:
47 |
48 | ```
49 | git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
50 | echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
51 | exec $SHELL
52 | rbenv install 2.3.1
53 | rbenv global 2.3.1
54 | gem install bundler
55 | rbenv rehash
56 | ruby -v
57 |
58 | # Rails
59 | curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
60 | sudo apt-get install -y nodejs
61 | gem install rails -v 4.2.6
62 | rbenv rehash
63 |
64 | # Jekyll
65 | gem install jekyll
66 | gem install github-pages
67 | gem install jekyll-sass-converter
68 |
69 | rbenv rehash
70 | ```
71 |
72 | ## Build and Serve
73 | After you cd into the docs folder (the base of the site) you can see the site locally by running the server with jekyll:
74 |
75 | ```bash
76 | cd docs/
77 | bundle exec jekyll serve
78 | ```
79 |
80 | or sometimes this works.
81 |
82 | ```
83 | jekyll serve
84 | ```
85 |
86 | And the site will be viewable at http://127.0.0.1:4000/singularity-cli/ . You can edit the markdown files in various folders to see changes in your browser, and push to your fork and then pull request to make changes. If you have a minor change, you can also do a patch directly in your browser from Github.
87 |
88 |
92 |
--------------------------------------------------------------------------------
/docs/pages/install.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Installation
4 | pdf: true
5 | permalink: /install
6 | toc: false
7 | ---
8 |
9 | # Installation Local
10 | You need `python3` and `pip3` in order to use this API.
11 |
12 | To install from the Github repository:
13 |
14 | ```bash
15 | git clone https://www.github.com/singularityhub/singularity-cli.git
16 | cd singularity-cli
17 | python3 setup.py install
18 | ```
19 |
20 | And you can also install from pip:
21 |
22 | ```bash
23 | # Client and Database
24 | pip3 install spython
25 | ```
26 |
27 |
28 | # Singularity
29 | You can also use our Singularity image provided, each directly from Singularity
30 | Hub, or via building on your own. To build a singularity container
31 |
32 | ```
33 | sudo singularity build spython Singularity
34 | ```
35 |
36 |
40 |
--------------------------------------------------------------------------------
/docs/pages/search.html:
--------------------------------------------------------------------------------
1 | ---
2 | title: Search
3 | sitemap: false
4 | permalink: /search
5 | not_editable: true
6 | ---
7 |
8 | Loading results for " "
9 |
10 |
11 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.black]
2 | profile = "black"
3 | exclude = ["^env/"]
4 |
5 | [tool.isort]
6 | profile = "black" # needed for black/isort compatibility
7 | skip = []
8 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | exclude = benchmarks docs
3 | max-line-length = 100
4 | ignore = E1 E2 E5 W5
5 | per-file-ignores =
6 | spython/__init__.py:F401
7 | spython/utils/__init__.py:F401
8 | spython/clint/__init__.py:F401
9 | spython/logger/__init__.py:F401
10 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from setuptools import find_packages, setup
4 |
5 | ################################################################################
6 | # HELPER FUNCTIONS #############################################################
7 | ################################################################################
8 |
9 |
10 | def get_lookup():
11 | """get version by way of singularity.version, returns a
12 | lookup dictionary with several global variables without
13 | needing to import spython
14 | """
15 | lookup = dict()
16 | version_file = os.path.join("spython", "version.py")
17 | with open(version_file) as filey:
18 | exec(filey.read(), lookup)
19 | return lookup
20 |
21 |
22 | def get_requirements(lookup=None):
23 | """get_requirements reads in requirements and versions from
24 | the lookup obtained with get_lookup"""
25 |
26 | if lookup is None:
27 | lookup = get_lookup()
28 |
29 | install_requires = []
30 | for module in lookup["INSTALL_REQUIRES"]:
31 | module_name = module[0]
32 | module_meta = module[1]
33 | if "exact_version" in module_meta:
34 | dependency = "%s==%s" % (module_name, module_meta["exact_version"])
35 | elif "min_version" in module_meta:
36 | if module_meta["min_version"] is None:
37 | dependency = module_name
38 | else:
39 | dependency = "%s>=%s" % (module_name, module_meta["min_version"])
40 | install_requires.append(dependency)
41 | return install_requires
42 |
43 |
44 | # Make sure everything is relative to setup.py
45 | install_path = os.path.dirname(os.path.abspath(__file__))
46 | os.chdir(install_path)
47 |
48 | # Get version information from the lookup
49 | lookup = get_lookup()
50 | VERSION = lookup["__version__"]
51 | NAME = lookup["NAME"]
52 | AUTHOR = lookup["AUTHOR"]
53 | AUTHOR_EMAIL = lookup["AUTHOR_EMAIL"]
54 | PACKAGE_URL = lookup["PACKAGE_URL"]
55 | KEYWORDS = lookup["KEYWORDS"]
56 | DESCRIPTION = lookup["DESCRIPTION"]
57 | LICENSE = lookup["LICENSE"]
58 | with open("README.md") as readme:
59 | LONG_DESCRIPTION = readme.read()
60 |
61 | ##########################################################################################
62 | # MAIN ###################################################################################
63 | ##########################################################################################
64 |
65 |
66 | if __name__ == "__main__":
67 | INSTALL_REQUIRES = get_requirements(lookup)
68 | TESTS_REQUIRES = get_requirements(lookup)
69 |
70 | setup(
71 | name=NAME,
72 | version=VERSION,
73 | author=AUTHOR,
74 | author_email=AUTHOR_EMAIL,
75 | maintainer=AUTHOR,
76 | maintainer_email=AUTHOR_EMAIL,
77 | packages=find_packages(),
78 | include_package_data=True,
79 | zip_safe=False,
80 | url=PACKAGE_URL,
81 | license=LICENSE,
82 | description=DESCRIPTION,
83 | long_description=LONG_DESCRIPTION,
84 | long_description_content_type="text/markdown",
85 | keywords=KEYWORDS,
86 | setup_requires=["pytest-runner"],
87 | tests_require=TESTS_REQUIRES,
88 | install_requires=INSTALL_REQUIRES,
89 | classifiers=[
90 | "Intended Audience :: Science/Research",
91 | "Intended Audience :: Developers",
92 | "Programming Language :: Python",
93 | "Topic :: Software Development",
94 | "Topic :: Scientific/Engineering",
95 | "Operating System :: Unix",
96 | "Programming Language :: Python :: 3",
97 | ],
98 | entry_points={"console_scripts": ["spython=spython.client:main"]},
99 | )
100 |
--------------------------------------------------------------------------------
/spython/README.md:
--------------------------------------------------------------------------------
1 | # Singularity Python Organization
2 | This will briefly outline the content of the folders here.
3 |
4 | ## Main Functions
5 |
6 | - [main](main) holds the primary client functions to interact with Singularity (e.g., exec, run, pull), and the subfolders within represent command groups (e.g., instance, image) along with the base of the client ([base](main/base)). This folder is where **commands** go!
7 | - [image](image.py) is a class that represents and holds an image object, in the case that the user wants to initialize a client with an image. This holds the ImageClass, which is only needed to instantiate an image.
8 | - [instance](instance) is a class that represents and holds an instance object. The user can instantiate the class, and then run it's sub functions to interact with it. The higher level "list" command is provided on the level of the client.
9 | - [cli](cli): is the actual entry point that connects the user to the python API client from the command line. The user inputs are parsed, and then passed into functions from main.
10 |
11 | ## Supporting
12 | - [utils](utils) are various file and other utilities shared across submodules.
13 | - [logger](logger) includes functions for progress bars, and logging levels.
14 | - [tests](tests) are important for continuous integration, but likely overlooked.
15 |
--------------------------------------------------------------------------------
/spython/__init__.py:
--------------------------------------------------------------------------------
1 | from spython.version import __version__
2 |
--------------------------------------------------------------------------------
/spython/client/recipe.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | import json
8 | import os
9 | import sys
10 |
11 | from spython.logger import bot
12 | from spython.main.parse.parsers import get_parser
13 | from spython.main.parse.writers import get_writer
14 | from spython.utils import write_file, write_json
15 |
16 |
17 | def main(args, options, parser):
18 | """This function serves as a wrapper around the DockerParser,
19 | SingularityParser, DockerWriter, and SingularityParser converters.
20 | We can either save to file if args.outfile is defined, or print
21 | to the console if not.
22 | """
23 | # We need something to work with
24 | if not args.files:
25 | parser.print_help()
26 | sys.exit(1)
27 |
28 | # Get the user specified input and output files
29 | outfile = None
30 | if len(args.files) > 1:
31 | outfile = args.files[1]
32 |
33 | # First try to get writer and parser, if not defined will return None
34 | writer = get_writer(args.writer)
35 | parser = get_parser(args.parser)
36 |
37 | # If the user wants to auto-detect the type
38 | if args.parser == "auto":
39 | if "dockerfile" in args.files[0].lower():
40 | parser = get_parser("docker")
41 | elif "singularity" in args.files[0].lower():
42 | parser = get_parser("singularity")
43 |
44 | # If the parser still isn't defined, no go.
45 | if parser is None:
46 | bot.exit(
47 | "Please provide a Dockerfile or Singularity recipe, or define the --parser type."
48 | )
49 |
50 | # If the writer needs auto-detect
51 | if args.writer == "auto":
52 | if parser.name == "docker":
53 | writer = get_writer("singularity")
54 | else:
55 | writer = get_writer("docker")
56 |
57 | # If the writer still isn't defined, no go
58 | if writer is None:
59 | bot.exit("Please define the --writer type.")
60 |
61 | # Initialize the chosen parser
62 | recipeParser = parser(args.files[0])
63 |
64 | # By default, discover entrypoint / cmd from Dockerfile
65 | entrypoint = "/bin/bash"
66 | force = False
67 |
68 | if args.entrypoint is not None:
69 | entrypoint = args.entrypoint
70 |
71 | # This is only done if the user intended to print json here
72 | recipeParser.entrypoint = args.entrypoint
73 | recipeParser.cmd = None
74 | force = True
75 |
76 | if args.json:
77 | if outfile is not None:
78 | if not os.path.exists(outfile):
79 | if force:
80 | write_json(outfile, recipeParser.recipe.json())
81 | else:
82 | bot.exit("%s exists, set --force to overwrite." % outfile)
83 | else:
84 | print(json.dumps(recipeParser.recipe.json(), indent=4))
85 |
86 | else:
87 | # Do the conversion
88 | recipeWriter = writer(recipeParser.recipe)
89 | result = recipeWriter.convert(runscript=entrypoint, force=force)
90 |
91 | # If the user specifies an output file, save to it
92 | if outfile is not None:
93 | write_file(outfile, result)
94 |
95 | # Otherwise, convert and print to screen
96 | else:
97 | print(result)
98 |
--------------------------------------------------------------------------------
/spython/client/shell.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def main(args, options, parser):
9 | # If we have options, first is image
10 | image = None
11 | if options:
12 | image = options.pop(0)
13 |
14 | lookup = {"ipython": ipython, "python": python, "bpython": run_bpython}
15 |
16 | shells = ["ipython", "python", "bpython"]
17 |
18 | # Otherwise present order of liklihood to have on system
19 | for shell in shells:
20 | try:
21 | return lookup[shell](image)
22 | except ImportError:
23 | pass
24 |
25 |
26 | def prepare_client(image):
27 | """prepare a client to embed in a shell with recipe parsers and writers."""
28 | # The client will announce itself (backend/database) unless it's get
29 | from spython.main import get_client
30 | from spython.main.parse import parsers, writers
31 |
32 | client = get_client()
33 |
34 | if image:
35 | client.load(image)
36 |
37 | # Add recipe parsers
38 | client.parsers = parsers
39 | client.writers = writers
40 | return client
41 |
42 |
43 | def ipython(image):
44 | """give the user an ipython shell"""
45 | client = prepare_client(image) # noqa
46 |
47 | try:
48 | from IPython import embed
49 | except ImportError:
50 | return python(image)
51 |
52 | embed()
53 |
54 |
55 | def run_bpython(image):
56 | """give the user a bpython shell"""
57 | client = prepare_client(image)
58 |
59 | try:
60 | import bpython
61 | except ImportError:
62 | return python(image)
63 |
64 | bpython.embed(locals_={"client": client})
65 |
66 |
67 | def python(image):
68 | """give the user a python shell"""
69 | import code
70 |
71 | client = prepare_client(image)
72 | code.interact(local={"client": client})
73 |
--------------------------------------------------------------------------------
/spython/client/test.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def main(args, options, parser):
9 | print("TBA, additional tests for Singularity containers.")
10 | print("What would you like to see? Let us know!")
11 | print("https://www.github.com/singularityhub/singularity-cli/issues")
12 |
--------------------------------------------------------------------------------
/spython/image.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | import hashlib
9 | import os
10 |
11 | from spython.logger import bot
12 | from spython.utils import split_uri
13 |
14 |
15 | class ImageBase:
16 | def __str__(self):
17 | protocol = getattr(self, "protocol", None)
18 | if protocol:
19 | return "%s://%s" % (protocol, self.image)
20 | return self.image
21 |
22 | def __repr__(self):
23 | return self.__str__()
24 |
25 | def parse_image_name(self, image):
26 | """
27 | simply split the uri from the image. Singularity handles
28 | parsing of registry, namespace, image.
29 |
30 | Parameters
31 | ==========
32 | image: the complete image uri to load (e.g., docker://ubuntu)
33 |
34 | """
35 | self._image = image
36 | self.protocol, self.image = split_uri(image)
37 |
38 |
39 | class Image(ImageBase):
40 | def __init__(self, image=None):
41 | """An image here is an image file or a record.
42 | The user can choose to load the image when starting the client, or
43 | update the main client with an image. The image object is kept
44 | with the main client to make running additional commands easier.
45 |
46 | Parameters
47 | ==========
48 | image: the image uri to parse (required)
49 |
50 | """
51 | super(Image, self).__init__()
52 | self.parse_image_name(image)
53 |
54 | def get_hash(self, image=None):
55 | """return an md5 hash of the file based on a criteria level. This
56 | is intended to give the file a reasonable version. This only is
57 | useful for actual image files.
58 |
59 | Parameters
60 | ==========
61 | image: the image path to get hash for (first priority). Second
62 | priority is image path saved with image object, if exists.
63 |
64 | """
65 | hasher = hashlib.md5()
66 | image = image or self.image
67 |
68 | if os.path.exists(image):
69 | with open(image, "rb") as f:
70 | for chunk in iter(lambda: f.read(4096), b""):
71 | hasher.update(chunk)
72 | return hasher.hexdigest()
73 |
74 | bot.warning("%s does not exist." % image)
75 |
--------------------------------------------------------------------------------
/spython/instance/cmd/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def generate_instance_commands():
9 | """The Instance client holds the Singularity Instance command group
10 | The levels of verbosity (debug and quiet) are passed from the main
11 | client via the environment variable MESSAGELEVEL.
12 |
13 | """
14 | from spython.instance import Instance
15 |
16 | # run_command uses run_cmd, but wraps to catch error
17 | from spython.main.base.command import init_command, run_command
18 | from spython.main.base.generate import RobotNamer
19 | from spython.main.base.logger import println
20 | from spython.main.instances import list_instances
21 |
22 | from .logs import _logs, error_logs, output_logs
23 | from .start import start
24 | from .stop import stop
25 |
26 | Instance.RobotNamer = RobotNamer()
27 | Instance._init_command = init_command
28 | Instance.run_command = run_command
29 | Instance._list = list_instances # list command is used to get metadata
30 | Instance._println = println
31 | Instance.start = start # intended to be called on init, not by user
32 | Instance.stop = stop
33 | Instance.error_logs = error_logs
34 | Instance.output_logs = output_logs
35 | Instance._logs = _logs
36 |
37 | # Give an instance the ability to breed :)
38 | Instance.instance = Instance
39 |
40 | return Instance
41 |
--------------------------------------------------------------------------------
/spython/instance/cmd/logs.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | import os
9 | import platform
10 |
11 | from spython.logger import bot
12 | from spython.utils import get_userhome, get_username
13 |
14 |
15 | def error_logs(self, print_logs=False):
16 | """For Singularity 3.5 and later, we are able to programatically
17 | derive the name of the log. In this case, return the content
18 | to the user. See
19 | https://github.com/sylabs/singularity/issues/1115#issuecomment-560457918
20 | for when this was added.
21 |
22 | Parameters
23 | ==========
24 | print_logs: boolean to indicate to print to the screen along with
25 | return (defaults to False to just return log string)
26 | """
27 | return self._logs(print_logs, "err")
28 |
29 |
30 | def output_logs(self, print_logs=False):
31 | """Get output logs for the user, if they exist.
32 |
33 | Parameters
34 | ==========
35 | print_logs: boolean to indicate to print to the screen along with
36 | return (defaults to False to just return log string)
37 | """
38 | return self._logs(print_logs, "out")
39 |
40 |
41 | def _logs(self, print_logs=False, ext="out"):
42 | """A shared function to print log files. The only differing element is
43 | the extension (err or out)
44 | """
45 | from spython.utils import check_install
46 |
47 | check_install()
48 |
49 | # Formulate the path of the logs
50 | hostname = platform.node()
51 | logpath = os.path.join(
52 | get_userhome(),
53 | ".singularity",
54 | "instances",
55 | "logs",
56 | hostname,
57 | get_username(),
58 | "%s.%s" % (self.name, ext),
59 | )
60 |
61 | if os.path.exists(logpath):
62 | with open(logpath, "r") as filey:
63 | logs = filey.read()
64 | if print_logs is True:
65 | print(logs)
66 | else:
67 | bot.warning("No log files have been produced.")
68 | return logs
69 |
--------------------------------------------------------------------------------
/spython/instance/cmd/start.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | from spython.logger import bot
9 |
10 |
11 | def start(
12 | self,
13 | image=None,
14 | name=None,
15 | args=None,
16 | sudo=False,
17 | sudo_options=None,
18 | options=None,
19 | capture=False,
20 | singularity_options=None,
21 | environ=None,
22 | quiet=True,
23 | ):
24 | """start an instance. This is done by default when an instance is created.
25 |
26 | Parameters
27 | ==========
28 | image: optionally, an image uri (if called as a command from Client)
29 | name: a name for the instance
30 | sudo: if the user wants to run the command with sudo
31 | capture: capture output, default is False. With True likely to hang.
32 | args: arguments to provide to the instance (supported Singularity 3.1+)
33 | singularity_options: a list of options to provide to the singularity client
34 | quiet: Do not print verbose output.
35 | options: a list of tuples, each an option to give to the start command
36 | [("--bind", "/tmp"),...]
37 |
38 | USAGE:
39 | singularity [...] instance.start [...]
40 |
41 | """
42 | from spython.utils import check_install, run_command
43 |
44 | check_install()
45 |
46 | # If name provided, over write robot (default)
47 | if name is not None:
48 | self.name = name
49 |
50 | # If an image isn't provided, we have an initialized instance
51 | if image is None:
52 | # Not having this means it was called as a command, without an image
53 | if not hasattr(self, "_image"):
54 | bot.exit("Please provide an image, or create an Instance first.")
55 |
56 | image = self._image
57 |
58 | cmd = self._init_command(["instance", "start"], singularity_options)
59 |
60 | # Set options and args
61 | args = args or self.args
62 | options = options or self.options
63 |
64 | # Add options, if they are provided
65 | if not isinstance(options, list):
66 | options = [] if options is None else options.split(" ")
67 |
68 | # Assemble the command!
69 | cmd = cmd + options + [image, self.name]
70 |
71 | # If arguments are provided
72 | if args is not None:
73 | if not isinstance(args, list):
74 | args = [args]
75 | cmd = cmd + args
76 |
77 | # Print verbose output
78 | if not (quiet or self.quiet):
79 | bot.info(" ".join(cmd))
80 |
81 | # Save the options and cmd, if the user wants to see them later
82 | self.options = options
83 | self.args = args
84 | self.cmd = cmd
85 |
86 | output = run_command(
87 | cmd,
88 | sudo=sudo,
89 | sudo_options=sudo_options,
90 | quiet=True,
91 | capture=capture,
92 | environ=environ,
93 | )
94 |
95 | if output["return_code"] == 0:
96 | self._update_metadata()
97 |
98 | else:
99 | message = "%s : return code %s" % (output["message"], output["return_code"])
100 | bot.error(message)
101 |
102 | return self
103 |
--------------------------------------------------------------------------------
/spython/instance/cmd/stop.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | from spython.logger import bot
9 |
10 |
11 | def stop(
12 | self,
13 | name=None,
14 | sudo=False,
15 | sudo_options=None,
16 | timeout=None,
17 | singularity_options=None,
18 | quiet=True,
19 | ):
20 | """stop an instance. This is done by default when an instance is created.
21 |
22 | Parameters
23 | ==========
24 | name: a name for the instance
25 | sudo: if the user wants to run the command with sudo
26 | singularity_options: a list of options to provide to the singularity client
27 | quiet: Do not print verbose output.
28 | timeout: forcebly kill non-stopped instance after the
29 | timeout specified in seconds
30 |
31 | USAGE:
32 | singularity [...] instance.stop [...]
33 |
34 | """
35 | from spython.utils import check_install, run_command
36 |
37 | check_install()
38 |
39 | subgroup = ["instance", "stop"]
40 | if timeout:
41 | subgroup += ["-t", str(timeout)]
42 |
43 | cmd = self._init_command(subgroup, singularity_options)
44 |
45 | # If name is provided assume referencing an instance
46 | instance_name = self.name
47 | if name is not None:
48 | instance_name = name
49 | cmd = cmd + [instance_name]
50 |
51 | # Print verbose output
52 | if not (quiet or self.quiet):
53 | bot.info(" ".join(cmd))
54 |
55 | output = run_command(cmd, sudo=sudo, sudo_options=sudo_options, quiet=True)
56 |
57 | if output["return_code"] != 0:
58 | message = "%s : return code %s" % (output["message"], output["return_code"])
59 | bot.error(message)
60 | return output["return_code"]
61 |
62 | return output["return_code"]
63 |
--------------------------------------------------------------------------------
/spython/logger/__init__.py:
--------------------------------------------------------------------------------
1 | from .compatibility import decodeUtf8String
2 | from .message import bot
3 | from .progress import ProgressBar
4 |
--------------------------------------------------------------------------------
/spython/logger/compatibility.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def decodeUtf8String(inputStr):
9 | """Convert an UTF8 sequence into a string
10 |
11 | Required for compatibility with Python 2 where str==bytes
12 | inputStr -- Either a str or bytes instance with UTF8 encoding
13 | """
14 | return (
15 | inputStr
16 | if isinstance(inputStr, str) or not isinstance(inputStr, bytes)
17 | else inputStr.decode("utf8")
18 | )
19 |
--------------------------------------------------------------------------------
/spython/logger/spinner.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | import sys
8 | import threading
9 | import time
10 | from random import choice
11 |
12 |
13 | class Spinner:
14 | spinning = False
15 | delay = 0.1
16 |
17 | @staticmethod
18 | def spinning_cursor():
19 | while 1:
20 | for cursor in "|/-\\":
21 | yield cursor
22 |
23 | @staticmethod
24 | def balloons_cursor():
25 | while 1:
26 | for cursor in ". o O @ *":
27 | yield cursor
28 |
29 | @staticmethod
30 | def changing_arrows():
31 | while 1:
32 | for cursor in "<^>v":
33 | yield cursor
34 |
35 | def select_generator(self, generator):
36 | if generator is None:
37 | generator = choice(["cursor", "arrow", "balloons"])
38 |
39 | return generator
40 |
41 | def __init__(self, delay=None, generator=None):
42 | generator = self.select_generator(generator)
43 |
44 | if generator == "cursor":
45 | self.spinner_generator = self.spinning_cursor()
46 | elif generator == "arrow":
47 | self.spinner_generator = self.changing_arrows()
48 | elif generator == "balloons":
49 | self.spinner_generator = self.balloons_cursor()
50 | if delay is None:
51 | delay = 0.2
52 | else:
53 | self.spinner_generator = self.spinning_cursor()
54 |
55 | if delay and float(delay):
56 | self.delay = delay
57 |
58 | def run(self):
59 | while self.spinning:
60 | sys.stdout.write(next(self.spinner_generator))
61 | sys.stdout.flush()
62 | time.sleep(self.delay)
63 | sys.stdout.write("\b")
64 | sys.stdout.flush()
65 |
66 | def start(self):
67 | self.spinning = True
68 | threading.Thread(target=self.run).start()
69 |
70 | def stop(self):
71 | self.spinning = False
72 | time.sleep(self.delay)
73 |
--------------------------------------------------------------------------------
/spython/main/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def get_client(quiet=False, debug=False):
9 | """
10 | get the client and perform imports not on init, in case there are any
11 | initialization or import errors.
12 |
13 | Parameters
14 | ==========
15 | quiet: if True, suppress most output about the client
16 | debug: turn on debugging mode
17 |
18 | """
19 | from .base import Client as client
20 |
21 | client.quiet = quiet
22 | client.debug = debug
23 |
24 | # Do imports here, can be customized
25 | from .apps import apps
26 | from .build import build
27 | from .execute import execute, shell
28 | from .export import export
29 | from .help import helpcmd
30 | from .inspect import inspect
31 | from .instances import list_instances, stopall # global instance commands
32 | from .pull import pull
33 | from .run import run
34 |
35 | # Actions
36 | client.apps = apps
37 | client.build = build
38 | client.execute = execute
39 | client.export = export
40 | client.help = helpcmd
41 | client.inspect = inspect
42 | client.instances = list_instances
43 | client.run = run
44 | client.shell = shell
45 | client.pull = pull
46 |
47 | # Commands Groups, Instances
48 | from spython.instance.cmd import ( # instance level commands
49 | generate_instance_commands,
50 | )
51 |
52 | client.instance = generate_instance_commands()
53 | client.instance_stopall = stopall
54 | client.instance.version = client.version
55 |
56 | # Commands Groups, OCI (Singularity version 3 and up)
57 | from spython.oci.cmd import generate_oci_commands
58 |
59 | client.oci = generate_oci_commands()() # first () runs function, second
60 | # initializes OciImage class
61 | client.oci.debug = client.debug
62 | client.oci.quiet = client.quiet
63 | client.oci.OciImage.quiet = client.quiet
64 | client.oci.OciImage.debug = client.debug
65 |
66 | # Initialize
67 | cli = client()
68 |
69 | # Pass on verbosity
70 | cli.instance.debug = cli.debug
71 | cli.instance.quiet = cli.quiet
72 | cli.instance.version = cli.version
73 |
74 | return cli
75 |
76 |
77 | Client = get_client()
78 |
--------------------------------------------------------------------------------
/spython/main/apps.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def apps(self, image=None, full_path=False, root=""):
9 | """
10 | return list of SCIF apps in image. The Singularity software serves
11 | a scientific filesystem integration that will install apps to
12 | /scif/apps and associated data to /scif/data. For more information
13 | about SCIF, see https://sci-f.github.io. Note that this seems
14 | to be deprecated in Singularity 3.x.
15 |
16 | Parameters
17 | ==========
18 | full_path: if True, return relative to scif base folder
19 | image_path: full path to the image
20 |
21 | """
22 | from spython.utils import check_install
23 |
24 | check_install()
25 |
26 | # No image provided, default to use the client's loaded image
27 | if image is None:
28 | image = self._get_uri()
29 |
30 | cmd = self._init_command("apps") + [image]
31 | output = self._run_command(cmd)
32 |
33 | if full_path:
34 | root = "/scif/apps/"
35 |
36 | if output:
37 | output = "".join(output).split("\n")
38 | output = ["%s%s" % (root, x) for x in output if x]
39 |
40 | return output
41 |
--------------------------------------------------------------------------------
/spython/main/base/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM continuumio/miniconda3
2 |
3 | #########################################
4 | # The Robot Namer
5 | #
6 | # docker build -t vanessa/robotname .
7 | # docker run vanessa/robotname
8 | #########################################
9 |
10 | LABEL maintainer vsochat@stanford.edu
11 | ADD generate.py /
12 | ENV PATH /usr/local/bin:$PATH
13 | ENTRYPOINT ["python", "/generate.py"]
14 |
--------------------------------------------------------------------------------
/spython/main/base/README.md:
--------------------------------------------------------------------------------
1 | # Robot Generator
2 |
3 | This folder contains a (sub-application) for a robot name generator.
4 |
5 | ## Docker
6 | It's built on Docker Hub, so you can run as:
7 |
8 | ```
9 | docker run vanessa/robotname
10 | ```
11 |
12 | or build locally first, with the present working directory as this folder.
13 |
14 | ```
15 | docker build -t vanessa/robotname .
16 | ```
17 |
18 | ```
19 | for i in `seq 1 10`;
20 | do
21 | docker run vanessa/robotname
22 | done
23 | boopy-peanut-butter-7311
24 | blank-snack-0334
25 | hello-buttface-6320
26 | chocolate-bicycle-9982
27 | frigid-frito-9511
28 | doopy-soup-7712
29 | phat-pancake-4952
30 | wobbly-kitty-3213
31 | lovely-mango-1987
32 | milky-poo-7960
33 | ```
34 |
35 | ## Singularity
36 |
37 | To build your image:
38 |
39 | ```
40 | sudo singularity build robotname Singularity
41 | ```
42 |
43 | or pull from Docker Hub :)
44 |
45 | ```
46 | singularity pull --name robotname docker://vanessa/robotname
47 | sregistry pull docker://vanessa/robotname
48 | ```
49 |
--------------------------------------------------------------------------------
/spython/main/base/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | from spython.logger import bot
9 | from spython.utils import check_install, get_singularity_version
10 |
11 | from .command import generate_bind_list, init_command, run_command
12 | from .flags import parse_verbosity
13 | from .generate import RobotNamer
14 | from .logger import init_level, println
15 | from .sutils import get_filename, get_uri, load, setenv
16 |
17 |
18 | class Client:
19 | def __str__(self):
20 | base = "[singularity-python]"
21 | if hasattr(self, "simage"):
22 | if self.simage.image not in [None, ""]:
23 | base = "%s[%s]" % (base, self.simage)
24 | return base
25 |
26 | def __repr__(self):
27 | return self.__str__()
28 |
29 | def __init__(self):
30 | """
31 | The base client for singularity, will have commands added to it.
32 | upon init, store verbosity requested in environment MESSAGELEVEL.
33 | """
34 | self._init_level()
35 |
36 | def version(self):
37 | """
38 | Shortcut to get_singularity_version, takes no arguments.
39 | """
40 | return get_singularity_version()
41 |
42 | def _check_install(self):
43 | """
44 | Ensure that singularity is installed, and exit if not.
45 | """
46 | if check_install() is not True:
47 | bot.exit("Cannot find Singularity! Is it installed?")
48 |
49 |
50 | # Image Utils
51 | Client.load = load
52 | Client._get_filename = get_filename
53 | Client._get_uri = get_uri
54 | Client.setenv = setenv
55 |
56 | # Commands
57 | Client._generate_bind_list = generate_bind_list
58 | Client._init_command = init_command
59 | Client._run_command = run_command
60 |
61 | # Flags and Logger
62 | Client._parse_verbosity = parse_verbosity
63 | Client._println = println
64 | Client._init_level = init_level
65 | Client.RobotNamer = RobotNamer()
66 |
--------------------------------------------------------------------------------
/spython/main/base/flags.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | """
9 | GLOBAL OPTIONS:
10 | -d|--debug Print debugging information
11 | -h|--help Display usage summary
12 | -s|--silent Only print errors
13 | -q|--quiet Suppress all normal output
14 | --version Show application version
15 | -v|--verbose Increase verbosity +1
16 | -x|--sh-debug Print shell wrapper debugging information
17 |
18 | GENERAL COMMANDS:
19 | help Show additional help for a command or container
20 | selftest Run some self tests for singularity install
21 |
22 | CONTAINER USAGE COMMANDS:
23 | exec Execute a command within container
24 | run Launch a runscript within container
25 | shell Run a Bourne shell within container
26 | test Launch a testscript within container
27 |
28 | CONTAINER MANAGEMENT COMMANDS:
29 | apps List available apps within a container
30 | bootstrap *Deprecated* use build instead
31 | build Build a new Singularity container
32 | check Perform container lint checks
33 | inspect Display container's metadata
34 | mount Mount a Singularity container image
35 | pull Pull a Singularity/Docker container to $PWD
36 | siflist list data object descriptors of a SIF container image
37 | sign Sign a group of data objects in container
38 | verify Verify the crypto signature of group of data objects in container
39 |
40 | COMMAND GROUPS:
41 | capability User's capabilities management command group
42 | image Container image command group
43 | instance Persistent instance command group
44 |
45 | """
46 |
47 |
48 | def parse_verbosity(self, args):
49 | """parse_verbosity will take an argument object, and return the args
50 | passed (from a dictionary) to a list
51 |
52 | Parameters
53 | ==========
54 | args: the argparse argument objects
55 |
56 | """
57 |
58 | flags = []
59 |
60 | if args.silent:
61 | flags.append("--silent")
62 | elif args.quiet:
63 | flags.append("--quiet")
64 | elif args.debug:
65 | flags.append("--debug")
66 | elif args.verbose:
67 | flags.append("-" + "v" * args.verbose)
68 |
69 | return flags
70 |
--------------------------------------------------------------------------------
/spython/main/base/logger.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | import os
9 |
10 | from spython.logger import decodeUtf8String
11 |
12 |
13 | def init_level(self, quiet=False):
14 | """set the logging level based on the environment
15 |
16 | Parameters
17 | ==========
18 | quiet: boolean if True, set to quiet. Gets overridden by environment
19 | setting, and only exists to define default
20 |
21 | """
22 |
23 | if os.environ.get("MESSAGELEVEL") == "QUIET":
24 | quiet = True
25 |
26 | self.quiet = quiet
27 |
28 |
29 | def println(self, output, quiet=False):
30 | """print will print the output, given that quiet is not True. This
31 | function also serves to convert output in bytes to utf-8
32 |
33 | Parameters
34 | ==========
35 | output: the string to print
36 | quiet: a runtime variable to over-ride the default.
37 |
38 | """
39 | if not self.quiet and not quiet:
40 | print(decodeUtf8String(output))
41 |
--------------------------------------------------------------------------------
/spython/main/base/sutils.py:
--------------------------------------------------------------------------------
1 | # Singularity Image utils for interacting with the Image/Instance
2 | # classes from the client
3 |
4 | # Copyright (C) 2017-2022 Vanessa Sochat.
5 |
6 | # This Source Code Form is subject to the terms of the
7 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
8 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 |
10 |
11 | import os
12 | import re
13 |
14 | from spython.logger import bot
15 |
16 |
17 | def load(self, image=None):
18 | """load an image, either an actual path on the filesystem or a uri.
19 |
20 | Parameters
21 | ==========
22 | image: the image path or uri to load (e.g., docker://ubuntu
23 |
24 | """
25 | from spython.image import Image
26 | from spython.instance import Instance
27 |
28 | self.simage = Image(image)
29 |
30 | if image is not None:
31 | if image.startswith("instance://"):
32 | self.simage = Instance(image)
33 | bot.info(self.simage)
34 |
35 |
36 | def setenv(self, variable, value):
37 | """set an environment variable for Singularity
38 |
39 | Parameters
40 | ==========
41 | variable: the variable to set
42 | value: the value to set
43 | """
44 | os.environ[variable] = value
45 | os.putenv(variable, value)
46 | bot.debug("%s set to %s" % (variable, value))
47 |
48 |
49 | def get_filename(self, image, ext="sif", pwd=True):
50 | """return an image filename based on the image uri.
51 |
52 | Parameters
53 | ==========
54 | ext: the extension to use
55 | pwd: derive a filename for the pwd
56 | """
57 | if pwd:
58 | image = os.path.basename(image)
59 | image = re.sub("^.*://", "", image)
60 | if not image.endswith(ext):
61 | image = "%s.%s" % (image, ext)
62 | return image
63 |
64 |
65 | def get_uri(self):
66 | """check if the loaded image object (self.simage) has an associated uri
67 | return if yes, None if not.
68 | """
69 | if hasattr(self, "simage"):
70 | if self.simage is not None:
71 | if self.simage.image not in ["", None]:
72 | # Concatenates the ://
73 | return str(self.simage)
74 |
--------------------------------------------------------------------------------
/spython/main/export.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | import os
8 |
9 | from spython.logger import bot
10 |
11 |
12 | def export(
13 | self,
14 | image_path,
15 | pipe=False,
16 | output_file=None,
17 | command=None,
18 | sudo=False,
19 | singularity_options=None,
20 | ):
21 | """export will export an image, sudo must be used. Since we have Singularity
22 | versions after 3, export is replaced with building into a sandbox.
23 |
24 | Parameters
25 | ==========
26 | image_path: full path to image
27 | pipe: export to pipe and not file (default, False)
28 | singularity_options: a list of options to provide to the singularity client
29 | output_file: if pipe=False, export tar to this file. If not specified,
30 | will generate temporary directory.
31 | """
32 | from spython.utils import check_install
33 |
34 | check_install()
35 |
36 | # If export is deprecated, we run a build
37 | bot.warning(
38 | "Export is not supported for Singularity 3.x. Building to sandbox instead."
39 | )
40 |
41 | if output_file is None:
42 | basename, _ = os.path.splitext(image_path)
43 | output_file = self._get_filename(basename, "sandbox", pwd=False)
44 |
45 | return self.build(
46 | recipe=image_path,
47 | image=output_file,
48 | sandbox=True,
49 | force=True,
50 | sudo=sudo,
51 | singularity_options=singularity_options,
52 | )
53 |
--------------------------------------------------------------------------------
/spython/main/help.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def helpcmd(self, command=None):
9 | """help prints the general function help, or help for a specific command
10 |
11 | Parameters
12 | ==========
13 | command: the command to get help for, if none, prints general help
14 |
15 | """
16 | from spython.utils import check_install
17 |
18 | check_install()
19 |
20 | cmd = ["singularity", "--help"]
21 | if command is not None:
22 | cmd.append(command)
23 | return self._run_command(cmd)
24 |
--------------------------------------------------------------------------------
/spython/main/inspect.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | import json as jsonp
9 |
10 | from spython.logger import bot
11 | from spython.utils import check_install, run_command
12 |
13 |
14 | def inspect(
15 | self, image=None, json=True, app=None, quiet=True, singularity_options=None
16 | ):
17 | """inspect will show labels, defile, runscript, and tests for an image
18 |
19 | Parameters
20 | ==========
21 | image: path of image to inspect
22 | json: print json instead of raw text (default True)
23 | quiet: Don't print result to the screen (default True)
24 | app: if defined, return help in context of an app
25 | singularity_options: a list of options to provide to the singularity client
26 |
27 | """
28 | check_install()
29 |
30 | # No image provided, default to use the client's loaded image
31 | if not image:
32 | image = self._get_uri()
33 |
34 | # If there still isn't an image, exit on error
35 | if not image:
36 | bot.exit("Please provide an image to inspect.")
37 |
38 | cmd = self._init_command("inspect", singularity_options)
39 | if app:
40 | cmd = cmd + ["--app", app]
41 |
42 | options = ["e", "d", "l", "r", "H", "t"]
43 | for x in options:
44 | cmd.append("-%s" % x)
45 |
46 | if json:
47 | cmd.append("--json")
48 |
49 | cmd.append(image)
50 |
51 | # Does the user want to see the command printed?
52 | if not (quiet or self.quiet):
53 | bot.info(" ".join(cmd))
54 |
55 | result = run_command(cmd, quiet=quiet)
56 |
57 | if result["return_code"] == 0:
58 | result = jsonp.loads(result["message"][0])
59 |
60 | # Unify output to singularity 3 format
61 | if "data" in result:
62 | result = result["data"]
63 |
64 | # Fix up labels
65 | result = parse_labels(result)
66 |
67 | if not quiet:
68 | print(jsonp.dumps(result, indent=4))
69 |
70 | return result
71 |
72 |
73 | def parse_labels(result):
74 | """fix up the labels, meaning parse to json if needed, and return
75 | original updated object
76 |
77 | Parameters
78 | ==========
79 | result: the json object to parse from inspect
80 | """
81 |
82 | labels = result["attributes"].get("labels") or {}
83 | try:
84 | labels = jsonp.loads(labels)
85 | except Exception:
86 | pass
87 |
88 | result["attributes"]["labels"] = labels
89 |
90 | return result
91 |
--------------------------------------------------------------------------------
/spython/main/parse/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/spython/main/parse/__init__.py
--------------------------------------------------------------------------------
/spython/main/parse/parsers/README.md:
--------------------------------------------------------------------------------
1 | # Parsers
2 |
3 | A parser class is intended to read in a container recipe file, and parse
4 | sections into a spython.main.recipe Recipe object. To create a new subclass
5 | of parser, you can copy one of the current (Docker or Singularity) as an
6 | example, and keep in mind the following:
7 |
8 | - The base class, `ParserBase` in [base.py](base.py) has already added an instantiated (and empty) Recipe() for the subclass to interact with (fill with content).
9 | - The subclass is encouraged to define the name (self.name) attribute for printing to the user.
10 | - The subclass should take the input file as an argument to pass the the ParserBase, which will handle reading in lines to a list self.lines.
11 | - The subclass should have a main method, parse, that when called will read the input file and populate the recipe (and return it to the user).
12 |
--------------------------------------------------------------------------------
/spython/main/parse/parsers/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | from .docker import DockerParser
8 | from .singularity import SingularityParser
9 |
10 |
11 | def get_parser(name):
12 | """get_parser is a simple helper function to return a parser based on it's
13 | name, if it exists. If there is no writer defined, we return None.
14 |
15 | Parameters
16 | ==========
17 | name: the name of the parser to return.
18 | """
19 | name = name.lower()
20 | parsers = {
21 | "docker": DockerParser,
22 | "singularity": SingularityParser,
23 | "dockerfile": DockerParser,
24 | }
25 |
26 | if name in parsers:
27 | return parsers[name]
28 |
--------------------------------------------------------------------------------
/spython/main/parse/recipe.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | class Recipe:
9 | """
10 | a recipe includes an environment, labels, runscript or command,
11 | and install sequence. This object is interacted with by a Parser
12 | (intended to popualte the recipe with content) and a Writer (intended
13 | to write a recipe to file). The parsers and writers are located in
14 | parsers.py, and writers.py, respectively. The user is also free to use
15 | the recipe class to build recipes.
16 |
17 | Parameters
18 | ==========
19 | recipe: the original recipe file, parsed by the subclass either
20 | DockerParser or SingularityParser
21 | layer: the count of the layer, for human readability
22 |
23 | """
24 |
25 | def __init__(self, recipe=None, layer=1):
26 | self.cmd = None
27 | self.comments = []
28 | self.entrypoint = None
29 | self.environ = []
30 | self.files = []
31 | self.layer_files = {}
32 | self.install = []
33 | self.labels = []
34 | self.ports = []
35 | self.test = None
36 | self.volumes = []
37 | self.workdir = None
38 | self.layer = layer
39 | self.fromHeader = None
40 |
41 | self.source = recipe
42 |
43 | def __str__(self):
44 | """show the user the recipe object, along with the type. E.g.,
45 |
46 | [spython-recipe][source:Singularity]
47 | [spython-recipe][source:Dockerfile]
48 |
49 | """
50 | base = "[spython-recipe]"
51 | if self.source:
52 | base = "%s[source:%s]" % (base, self.source)
53 | return base
54 |
55 | def json(self):
56 | """return a dictionary version of the recipe, intended to be parsed
57 | or printed as json.
58 |
59 | Returns: a dictionary of attributes including cmd, comments,
60 | entrypoint, environ, files, install, labels, ports,
61 | test, volumes, and workdir, organized by layer for
62 | multistage builds.
63 | """
64 | attributes = [
65 | "cmd",
66 | "comments",
67 | "entrypoint",
68 | "environ",
69 | "files",
70 | "fromHeader",
71 | "layer_files",
72 | "install",
73 | "labels",
74 | "ports",
75 | "test",
76 | "volumes",
77 | "workdir",
78 | ]
79 |
80 | result = {}
81 |
82 | for attrib in attributes:
83 | value = getattr(self, attrib)
84 | if value:
85 | result[attrib] = value
86 |
87 | return result
88 |
89 | def __repr__(self):
90 | return self.__str__()
91 |
--------------------------------------------------------------------------------
/spython/main/parse/writers/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | from .docker import DockerWriter
9 | from .singularity import SingularityWriter
10 |
11 |
12 | def get_writer(name):
13 | """get_writer is a simple helper function to return a writer based on it's
14 | name, if it exists. If there is no writer defined, we return None.
15 |
16 | Parameters
17 | ==========
18 | name: the name of the writer to return.
19 | """
20 | name = name.lower()
21 | writers = {
22 | "docker": DockerWriter,
23 | "singularity": SingularityWriter,
24 | "dockerfile": DockerWriter,
25 | }
26 |
27 | if name in writers:
28 | return writers[name]
29 |
--------------------------------------------------------------------------------
/spython/main/parse/writers/base.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | import os
9 | import tempfile
10 |
11 | from spython.logger import bot
12 | from spython.utils import write_file
13 |
14 |
15 | class WriterBase:
16 | def __init__(self, recipe=None):
17 | """a writer base will take a recipe object (parser.base.Recipe) and
18 | provide helpers for writing to file.
19 |
20 | Parameters
21 | ==========
22 | recipe: the recipe instance to parse
23 |
24 | """
25 | self.recipe = recipe
26 |
27 | def write(self, output_file=None, force=False):
28 | """convert a recipe to a specified format, and write to file, meaning
29 | we use the loaded recipe to write to an output file.
30 | If the output file is not specified, a temporary file is used.
31 |
32 | Parameters
33 | ==========
34 | output_file: the file to save to, not required (estimates default)
35 | force: if True, if file exists, over-write existing file
36 |
37 | """
38 | if output_file is None:
39 | output_file = self._get_conversion_outfile()
40 |
41 | # Cut out early if file exists and we aren't overwriting
42 | if os.path.exists(output_file) and not force:
43 | bot.exit("%s exists, and force is False." % output_file)
44 |
45 | # Do the conversion if function is provided by subclass
46 | if hasattr(self, "convert"):
47 | converted = self.convert()
48 | bot.info("Saving to %s" % output_file)
49 | write_file(output_file, converted)
50 |
51 | def _get_conversion_outfile(self):
52 | """a helper function to return a conversion temporary output file
53 | based on kind of conversion
54 |
55 | Parameters
56 | ==========
57 | convert_to: a string either docker or singularity, if a different
58 |
59 | """
60 | prefix = "spythonRecipe"
61 | if hasattr(self, "name"):
62 | prefix = self.name
63 | suffix = next(tempfile._get_candidate_names())
64 | return "%s.%s" % (prefix, suffix)
65 |
66 | # Printing
67 |
68 | def __str__(self):
69 | """show the user the recipe object, along with the type. E.g.,
70 |
71 | [spython-writer][docker]
72 | [spython-writer][singularity]
73 |
74 | """
75 | base = "[spython-writer]"
76 | if hasattr(self, "name"):
77 | base = "%s[%s]" % (base, self.name)
78 | return base
79 |
80 | def __repr__(self):
81 | return self.__str__()
82 |
--------------------------------------------------------------------------------
/spython/main/pull.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | import os
8 | import re
9 |
10 | from spython.logger import bot
11 | from spython.utils import ScopedEnvVar, stream_command
12 |
13 |
14 | def pull(
15 | self,
16 | image=None,
17 | name=None,
18 | pull_folder="",
19 | ext="sif",
20 | force=False,
21 | capture=False,
22 | stream=False,
23 | quiet=False,
24 | singularity_options=None,
25 | ):
26 | """pull will pull a singularity hub or Docker image
27 |
28 | Parameters
29 | ==========
30 | image: the complete image uri. If not provided, the client loaded is used
31 | singularity_options: a list of options to provide to the singularity client
32 | pull_folder: if not defined, pulls to $PWD (''). If defined, pulls to
33 | user specified location instead.
34 |
35 | Docker and Singularity Hub Naming
36 | ---------------------------------
37 | name: a custom name to use, to override default
38 | ext: if no name specified, the default extension to use.
39 |
40 | """
41 | from spython.utils import check_install
42 |
43 | check_install()
44 |
45 | cmd = self._init_command("pull", singularity_options)
46 |
47 | # Quiet is honored if set by the client, or user
48 | quiet = quiet or self.quiet
49 |
50 | # No image provided, default to use the client's loaded image
51 | if image is None:
52 | image = self._get_uri()
53 |
54 | # If it's still None, no go!
55 | if image is None:
56 | bot.exit("You must provide an image uri, or use client.load() first.")
57 |
58 | # Singularity Only supports shub, docker and library pull
59 | if not re.search("^(shub|docker|library|https|oras)://", image):
60 | bot.exit("pull only valid for docker, oras, https, shub and library.")
61 |
62 | # If we still don't have a custom name, base off of image uri.
63 | if name is None:
64 | name = self._get_filename(image, ext)
65 |
66 | if pull_folder:
67 | final_image = os.path.join(pull_folder, os.path.basename(name))
68 |
69 | # Regression Singularity 3.* onward, PULLFOLDER not honored
70 | # https://github.com/sylabs/singularity/issues/2788
71 | name = final_image
72 | pull_folder = None # Don't use pull_folder
73 | else:
74 | final_image = name
75 |
76 | cmd = cmd + ["--name", name]
77 |
78 | if force:
79 | cmd = cmd + ["--force"]
80 |
81 | cmd.append(image)
82 |
83 | if not quiet:
84 | bot.info(" ".join(cmd))
85 |
86 | with ScopedEnvVar("SINGULARITY_PULLFOLDER", pull_folder):
87 | # Option 1: Streaming we just run to show user
88 | if not stream:
89 | self._run_command(cmd, capture=capture, quiet=quiet)
90 |
91 | # Option 3: A custom name we can predict (not commit/hash) and can also show
92 | else:
93 | # As of Singularity 3.x (at least 3.8) output goes to stderr
94 | return final_image, stream_command(cmd, sudo=False, output_type="stderr")
95 |
96 | if os.path.exists(final_image) and not quiet:
97 | bot.info(final_image)
98 | return final_image
99 |
--------------------------------------------------------------------------------
/spython/main/run.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | import json
8 |
9 | from spython.logger import bot
10 | from spython.utils import stream_command
11 |
12 |
13 | def run(
14 | self,
15 | image=None,
16 | args=None,
17 | app=None,
18 | sudo=False,
19 | writable=False,
20 | contain=False,
21 | bind=None,
22 | stream=False,
23 | nv=False,
24 | options=None,
25 | singularity_options=None,
26 | return_result=False,
27 | quiet=False,
28 | background=False,
29 | stream_type="stdout",
30 | ):
31 | """
32 | run will run the container, with or withour arguments (which
33 | should be provided in a list)
34 |
35 | Parameters
36 | ==========
37 | image: full path to singularity image
38 | args: args to include with the run
39 | app: if not None, execute a command in context of an app
40 | writable: This option makes the file system accessible as read/write
41 | options: an optional list of options to provide to run.
42 | singularity_options: a list of options to provide to the singularity client
43 | contain: This option disables the automatic sharing of writable
44 | filesystems on your host
45 | bind: list or single string of bind paths.
46 | This option allows you to map directories on your host system to
47 | directories within your container using bind mounts
48 | stream: if True, return for the user to run
49 | nv: if True, load Nvidia Drivers in runtime (default False)
50 | return_result: if True, return entire json object with return code
51 | and message result (default is False)
52 | quiet: print the command to the user
53 | stream_type: Sets which output stream from the singularity command should be return. Values are 'stdout', 'stderr', 'both'.
54 | """
55 | from spython.utils import check_install
56 |
57 | check_install()
58 |
59 | cmd = self._init_command("run", singularity_options)
60 |
61 | # Does the user want to see the command printed?
62 | quiet = quiet or self.quiet
63 |
64 | # nv option leverages any GPU cards
65 | if nv:
66 | cmd += ["--nv"]
67 |
68 | # No image provided, default to use the client's loaded image
69 | if image is None:
70 | image = self._get_uri()
71 |
72 | # If an instance is provided, grab it's name
73 | if isinstance(image, self.instance):
74 | image = image.get_uri()
75 |
76 | # If image is still None, not defined by user or previously with client
77 | if image is None:
78 | bot.exit("Please load or provide an image.")
79 |
80 | # Does the user want to use bind paths option?
81 | if bind is not None:
82 | cmd += self._generate_bind_list(bind)
83 |
84 | # Does the user want to run an app?
85 | if app is not None:
86 | cmd = cmd + ["--app", app]
87 |
88 | # Does the user want writable?
89 | if writable:
90 | cmd.append("--writable")
91 |
92 | # Add options
93 | if options is not None:
94 | cmd = cmd + options
95 |
96 | cmd = cmd + [image]
97 |
98 | if args is not None:
99 | if not isinstance(args, list):
100 | args = args.split(" ")
101 | cmd = cmd + args
102 |
103 | if not quiet:
104 | bot.info(" ".join(cmd))
105 |
106 | if background:
107 | return self._run_command(cmd, sudo=sudo, background=True)
108 |
109 | elif not stream:
110 | result = self._run_command(cmd, sudo=sudo, return_result=return_result)
111 | else:
112 | return stream_command(cmd, sudo=sudo, output_type=stream_type)
113 |
114 | # If the user wants the raw result object
115 | if return_result:
116 | return result
117 |
118 | # Otherwise, we parse the result if it was successful
119 | if result:
120 | result = result.strip("\n")
121 |
122 | try:
123 | result = json.loads(result)
124 | except Exception:
125 | pass
126 | return result
127 |
--------------------------------------------------------------------------------
/spython/oci/README.md:
--------------------------------------------------------------------------------
1 | # OCI Development
2 |
3 | Here I'll write how I created an OCI bundle using Singularity to help with
4 | development of the client. First, notice the [config.json](config.json)
5 | in the present working directory - it's a general configuration for OCI
6 | runtime specification (version 1.0) that we can put into a bundle (a folder
7 | that will serve as the root of a container). Here is how I did that.
8 |
9 | First, we are developing with the first release of Singularity that supports
10 | OCI:
11 |
12 | ```bash
13 | $ singularity --version
14 | singularity version 3.1.0-rc2.28.ga72e427
15 | ```
16 |
17 | Next, we are going to create a bundle. A bundle is a folder that we will
18 | treat as the root of our container filesystem. The easiest way to do
19 | this is to dump a filesystem there from another container.
20 |
21 | ```bash
22 | $ singularity build --sandbox /tmp/bundle docker://ubuntu:18.04
23 | $ cp config.json /tmp/bundle
24 | ```
25 |
26 | The purpose of the build is only to dump a complete operating system into the
27 | bundle folder. The configuration file then is to conform to the Oci
28 | Runtime specification.
29 |
30 | We can then test interaction with the OCI client of Singularity python
31 | by providing the bundle directory at /tmp/bundle.
32 |
--------------------------------------------------------------------------------
/spython/oci/cmd/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def generate_oci_commands():
9 | """The oci command group will allow interaction with an image using
10 | OCI commands.
11 | """
12 | # run_command uses run_cmd, but wraps to catch error
13 | from spython.main.base.command import run_command, send_command
14 | from spython.main.base.generate import RobotNamer
15 | from spython.main.base.logger import println
16 | from spython.oci import OciImage
17 |
18 | from .actions import _run, attach, create, delete, execute, run, update
19 |
20 | # Oci Command Groups
21 | from .mounts import mount, umount
22 | from .states import _state_command, kill, pause, resume, start, state
23 |
24 | # Oci Commands
25 | OciImage.start = start
26 | OciImage.mount = mount
27 | OciImage.umount = umount
28 | OciImage.state = state
29 | OciImage.resume = resume
30 | OciImage.pause = pause
31 | OciImage.attach = attach
32 | OciImage.create = create
33 | OciImage.delete = delete
34 | OciImage.execute = execute
35 | OciImage.update = update
36 | OciImage.kill = kill
37 | OciImage.run = run
38 | OciImage._run = _run
39 | OciImage._state_command = _state_command
40 |
41 | OciImage.RobotNamer = RobotNamer()
42 | OciImage._send_command = send_command # send and disregard stderr, stdout
43 | OciImage._run_command = run_command
44 | OciImage._println = println
45 | OciImage.OciImage = OciImage
46 |
47 | return OciImage
48 |
--------------------------------------------------------------------------------
/spython/oci/cmd/mounts.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2019-2022 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | def mount(self, image, sudo=None):
9 | """create an OCI bundle from SIF image
10 |
11 | Parameters
12 | ==========
13 | image: the container (sif) to mount
14 | """
15 | return self._state_command(image, command="mount", sudo=sudo)
16 |
17 |
18 | def umount(self, image, sudo=None):
19 | """delete an OCI bundle created from SIF image
20 |
21 | Parameters
22 | ==========
23 | image: the container (sif) to mount
24 | """
25 | return self._state_command(image, command="umount", sudo=sudo)
26 |
--------------------------------------------------------------------------------
/spython/oci/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "ociVersion": "1.0.1",
3 | "process": {
4 | "terminal": true,
5 | "user": {
6 | "uid": 1,
7 | "gid": 1
8 | },
9 | "args": [
10 | "sh"
11 | ],
12 | "env": [
13 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
14 | "TERM=xterm"
15 | ],
16 | "cwd": "/",
17 | "noNewPrivileges": true
18 | },
19 | "root": {
20 | "path": ".",
21 | "readonly": true
22 | },
23 | "hostname": "slartibartfast",
24 | "mounts": [
25 | {
26 | "destination": "/proc",
27 | "type": "proc",
28 | "source": "proc"
29 | },
30 | {
31 | "destination": "/dev/pts",
32 | "type": "devpts",
33 | "source": "devpts"
34 | },
35 | {
36 | "destination": "/dev/shm",
37 | "type": "tmpfs",
38 | "source": "shm"
39 | },
40 | {
41 | "destination": "/dev/mqueue",
42 | "type": "mqueue",
43 | "source": "mqueue"
44 | },
45 | {
46 | "destination": "/sys",
47 | "type": "sysfs",
48 | "source": "sysfs"
49 | },
50 | {
51 | "destination": "/sys/fs/cgroup",
52 | "type": "cgroup",
53 | "source": "cgroup"
54 | }
55 | ],
56 | "linux": {
57 | "uidMappings": [
58 | {
59 | "containerID": 0,
60 | "hostID": 1000,
61 | "size": 32000
62 | }
63 | ],
64 | "gidMappings": [
65 | {
66 | "containerID": 0,
67 | "hostID": 1000,
68 | "size": 32000
69 | }
70 | ],
71 | "rootfsPropagation": "slave",
72 | "namespaces": [
73 | {
74 | "type": "pid"
75 | },
76 | {
77 | "type": "network"
78 | },
79 | {
80 | "type": "ipc"
81 | },
82 | {
83 | "type": "uts"
84 | },
85 | {
86 | "type": "mount"
87 | },
88 | {
89 | "type": "user"
90 | },
91 | {
92 | "type": "cgroup"
93 | }
94 | ],
95 | "maskedPaths": [
96 | "/proc/kcore",
97 | "/proc/latency_stats",
98 | "/proc/timer_stats",
99 | "/proc/sched_debug"
100 | ],
101 | "readonlyPaths": [
102 | "/proc/asound",
103 | "/proc/bus",
104 | "/proc/fs",
105 | "/proc/irq",
106 | "/proc/sys",
107 | "/proc/sysrq-trigger"
108 | ]
109 | },
110 | "annotations": {
111 | "com.example.key1": "value1",
112 | "com.example.key2": "value2"
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/spython/tests/Xtest_oci.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Copyright (C) 2017-2024 Vanessa Sochat.
4 |
5 | # This Source Code Form is subject to the terms of the
6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 |
9 | import os
10 | import shutil
11 |
12 | import pytest
13 |
14 | from spython.main import Client
15 | from spython.main.base.generate import RobotNamer
16 | from spython.utils import get_installdir
17 |
18 |
19 | @pytest.fixture
20 | def sandbox(tmp_path):
21 | image = Client.build(
22 | "docker://busybox:1.30.1",
23 | image=str(tmp_path / "sandbox"),
24 | sandbox=True,
25 | sudo=False,
26 | )
27 |
28 | assert os.path.exists(image)
29 |
30 | config = os.path.join(get_installdir(), "oci", "config.json")
31 | shutil.copyfile(config, os.path.join(image, "config.json"))
32 | return image
33 |
34 |
35 | def test_oci_image():
36 | image = Client.oci.OciImage("oci://imagename")
37 | assert image.get_uri() == "[singularity-python-oci:oci://imagename]"
38 |
39 |
40 | def test_oci(sandbox): # pylint: disable=redefined-outer-name
41 | image = sandbox
42 | container_id = RobotNamer().generate()
43 |
44 | # A non existing process should not have a state
45 | print("...Case 1. Check status of non-existing bundle.")
46 | state = Client.oci.state("mycontainer")
47 | assert state is None
48 |
49 | # This will use sudo
50 | print("...Case 2: Create OCI image from bundle")
51 | result = Client.oci.create(bundle=image, container_id=container_id)
52 |
53 | print(result)
54 | assert result["status"] == "created"
55 |
56 | print("...Case 3. Execute command to non running bundle.")
57 | result = Client.oci.execute(
58 | container_id=container_id, sudo=True, command=["ls", "/"]
59 | )
60 |
61 | print(result)
62 | assert "bin" in result
63 |
64 | print("...Case 4. Start container return value 0.")
65 | state = Client.oci.start(container_id, sudo=True)
66 | assert state == 0
67 |
68 | print("...Case 5. Execute command to running bundle.")
69 | result = Client.oci.execute(
70 | container_id=container_id, sudo=True, command=["ls", "/"]
71 | )
72 |
73 | print(result)
74 | assert "bin" in result
75 |
76 | print("...Case 6. Check status of existing bundle.")
77 | state = Client.oci.state(container_id, sudo=True)
78 | assert state["status"] == "running"
79 |
80 | print("...Case 7. Pause running container return value 0.")
81 | state = Client.oci.pause(container_id, sudo=True)
82 | assert state == 0
83 |
84 | # State was still reported as running
85 | print("...check status of paused bundle.")
86 | state = Client.oci.state(container_id, sudo=True)
87 | assert state["status"] == "paused"
88 |
89 | print("...Case 8. Resume paused container return value 0.")
90 | state = Client.oci.resume(container_id, sudo=True)
91 | assert state == 0
92 |
93 | print("...check status of resumed bundle.")
94 | state = Client.oci.state(container_id, sudo=True)
95 | assert state["status"] == "running"
96 |
97 | print("...Case 9. Kill container.")
98 | state = Client.oci.kill(container_id, sudo=True)
99 | assert state == 0
100 |
101 | # Clean up the image (should still use sudo)
102 | # Bug in singularity that kill doesn't kill completely - this returns
103 | # 255. When testsupdated to 3.1.* add signal=K to run
104 | result = Client.oci.delete(container_id, sudo=True)
105 | assert result in [0, 255]
106 |
--------------------------------------------------------------------------------
/spython/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/singularityhub/singularity-cli/a2e013f44c945fc514033522cdc69ee99b7d0025/spython/tests/__init__.py
--------------------------------------------------------------------------------
/spython/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import os
2 | from glob import glob
3 |
4 | import pytest
5 |
6 | from spython.main import Client
7 | from spython.utils import get_installdir
8 |
9 |
10 | @pytest.fixture
11 | def installdir():
12 | return get_installdir()
13 |
14 |
15 | @pytest.fixture
16 | def test_data(installdir): # pylint: disable=redefined-outer-name
17 | root = os.path.join(installdir, "tests", "testdata")
18 | dockerFiles = glob(os.path.join(root, "docker2singularity", "*.docker"))
19 | singularityFiles = glob(os.path.join(root, "singularity2docker", "*.def"))
20 | return {
21 | "root": root,
22 | "d2s": [(file, os.path.splitext(file)[0] + ".def") for file in dockerFiles],
23 | "s2d": [
24 | (file, os.path.splitext(file)[0] + ".docker") for file in singularityFiles
25 | ],
26 | }
27 |
28 |
29 | @pytest.fixture(scope="session")
30 | def oras_container(tmp_path_factory):
31 | folder = tmp_path_factory.mktemp("oras-img")
32 | return folder, Client.pull(
33 | "oras://ghcr.io/singularityhub/github-ci:latest", pull_folder=str(folder)
34 | )
35 |
36 |
37 | @pytest.fixture(scope="session")
38 | def docker_container(tmp_path_factory):
39 | folder = tmp_path_factory.mktemp("docker-img")
40 | return folder, Client.pull("docker://busybox:1.30.1", pull_folder=str(folder))
41 |
--------------------------------------------------------------------------------
/spython/tests/helpers.sh:
--------------------------------------------------------------------------------
1 | runTest() {
2 |
3 | # The first argument is the code we should get
4 | ERROR="${1:-}"
5 | shift
6 | OUTPUT=${1:-}
7 | shift
8 |
9 | "$@" > "${OUTPUT}" 2>&1
10 | RETVAL="$?"
11 |
12 | if [ "$ERROR" = "0" -a "$RETVAL" != "0" ]; then
13 | echo "$@ (retval=$RETVAL) ERROR"
14 | cat ${OUTPUT}
15 | echo "Output in ${OUTPUT}"
16 | exit 1
17 | elif [ "$ERROR" != "0" -a "$RETVAL" = "0" ]; then
18 | echo "$@ (retval=$RETVAL) ERROR"
19 | echo "Output in ${OUTPUT}"
20 | cat ${OUTPUT}
21 | exit 1
22 | else
23 | echo "$@ (retval=$RETVAL) OK"
24 | fi
25 | }
26 |
--------------------------------------------------------------------------------
/spython/tests/test_base.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Copyright (C) 2017-2024 Vanessa Sochat.
4 |
5 | # This Source Code Form is subject to the terms of the
6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 |
9 | from spython.image import Image
10 |
11 |
12 | def test_image():
13 | image = Image("docker://ubuntu")
14 | assert str(image) == "docker://ubuntu"
15 | assert image.protocol == "docker"
16 | assert image.image == "ubuntu"
17 |
--------------------------------------------------------------------------------
/spython/tests/test_client.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Copyright (C) 2017-2024 Vanessa Sochat.
4 |
5 | # This Source Code Form is subject to the terms of the
6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 |
9 |
10 | import os
11 | import shutil
12 | from subprocess import CalledProcessError
13 |
14 | import pytest
15 |
16 | from spython.main import Client
17 | from spython.utils import write_file
18 |
19 |
20 | def test_build_from_docker(tmp_path):
21 | container = str(tmp_path / "container.sif")
22 |
23 | created_container = Client.build(
24 | "docker://busybox:1.30.1", image=container, sudo=False
25 | )
26 | assert created_container == container
27 | assert os.path.exists(created_container)
28 |
29 |
30 | def test_export():
31 | sandbox = "busybox:1.30.sandbox"
32 | created_sandbox = Client.export("docker://busybox:1.30.1")
33 | assert created_sandbox == sandbox
34 | assert os.path.exists(created_sandbox)
35 | shutil.rmtree(created_sandbox)
36 |
37 |
38 | def test_docker_pull(docker_container):
39 | tmp_path, container = docker_container
40 | print(container)
41 | assert container == str(tmp_path / ("busybox:1.30.1.sif"))
42 | assert os.path.exists(container)
43 |
44 |
45 | def test_oras_pull(oras_container):
46 | tmp_path, container = oras_container
47 | print(container)
48 | assert container == str(tmp_path / ("github-ci:latest.sif"))
49 | assert os.path.exists(container)
50 |
51 |
52 | def test_execute(docker_container):
53 | result = Client.execute(docker_container[1], "ls /")
54 | print(result)
55 | if isinstance(result, list):
56 | result = "".join(result)
57 | assert "tmp\nusr\nvar" in result
58 |
59 |
60 | def test_execute_with_return_code(docker_container):
61 | result = Client.execute(docker_container[1], "ls /", return_result=True)
62 | print(result)
63 | if isinstance(result["message"], list):
64 | result["message"] = "".join(result["message"])
65 | assert "tmp\nusr\nvar" in result["message"]
66 | assert result["return_code"] == 0
67 |
68 |
69 | def test_execute_with_stream(docker_container):
70 | output = Client.execute(docker_container[1], "ls /", stream=True)
71 | message = "".join(list(output))
72 | assert "tmp\nusr\nvar" in message
73 |
74 | output = Client.execute(
75 | docker_container[1], "ls /", stream=True, stream_type="both"
76 | )
77 | message = "".join(list(output))
78 | assert "tmp\nusr\nvar" in message
79 |
80 | # ls / should be successful, so there will be no stderr
81 | output = Client.execute(
82 | docker_container[1], "ls /", stream=True, stream_type="stderr"
83 | )
84 | message = "".join(list(output))
85 | assert "tmp\nusr\nvar" not in message
86 |
87 |
88 | @pytest.mark.parametrize("return_code", [True, False])
89 | def test_execute_with_called_process_error(
90 | capsys, docker_container, return_code, tmp_path
91 | ):
92 | tmp_file = os.path.join(tmp_path, "CalledProcessError.sh")
93 | # "This is stdout" to stdout, "This is stderr" to stderr
94 | script = f"""#!/bin/bash
95 | echo "This is stdout"
96 | >&2 echo "This is stderr"
97 | {"exit 1" if return_code else ""}
98 | """
99 | write_file(tmp_file, script)
100 | if return_code:
101 | with pytest.raises(CalledProcessError):
102 | for line in Client.execute(
103 | docker_container[1], f"/bin/sh {tmp_file}", stream=True
104 | ):
105 | print(line, "")
106 | else:
107 | for line in Client.execute(
108 | docker_container[1], f"/bin/sh {tmp_file}", stream=True
109 | ):
110 | print(line, "")
111 | captured = capsys.readouterr()
112 | assert "stdout" in captured.out
113 | if return_code:
114 | assert "stderr" in captured.err
115 | else:
116 | assert "stderr" not in captured.err
117 |
118 |
119 | def test_inspect(docker_container):
120 | result = Client.inspect(docker_container[1])
121 | assert "attributes" in result or "data" in result
122 |
--------------------------------------------------------------------------------
/spython/tests/test_client.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Include help functions
4 | . helpers.sh
5 |
6 | echo
7 | echo "************** START: test_client.sh **********************"
8 |
9 | # Create temporary testing directory
10 | echo "Creating temporary directory to work in."
11 | tmpdir=$(mktemp -d)
12 | output=$(mktemp ${tmpdir:-/tmp}/spython_test.XXXXXX)
13 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
14 |
15 | echo "Testing help commands..."
16 |
17 | # Test help for all commands
18 | for command in recipe shell;
19 | do
20 | runTest 0 $output spython $command --help
21 | done
22 |
23 | echo "#### Testing recipe auto generation"
24 | runTest 1 $output spython recipe $here/testdata/Dockerfile | grep "FROM"
25 | runTest 0 $output spython recipe $here/testdata/Dockerfile | grep "%post"
26 | runTest 1 $output spython recipe $here/testdata/Singularity | grep "%post"
27 | runTest 0 $output spython recipe $here/testdata/Singularity | grep "FROM"
28 |
29 | echo "#### Testing recipe targeted generation"
30 | runTest 0 $output spython recipe --writer docker $here/testdata/Dockerfile | grep "FROM"
31 | runTest 1 $output spython recipe --writer docker $here/testdata/Dockerfile | grep "%post"
32 | runTest 0 $output spython recipe --writer singularity $here/testdata/Singularity | grep "%post"
33 | runTest 1 $output spython recipe --writer singularity $here/testdata/Singularity | grep "FROM"
34 |
35 | echo "#### Testing recipe file generation"
36 | outfile=$(mktemp ${tmpdir:-/tmp}/spython_recipe.XXXXXX)
37 | runTest 0 $output spython recipe $here/testdata/Dockerfile $outfile
38 | runTest 0 $output test -f "$outfile"
39 | runTest 0 $output cat $outfile | grep "%post"
40 | rm $outfile
41 |
42 | echo "#### Testing recipe json export"
43 | runTest 0 $output spython recipe --json $here/testdata/Dockerfile | grep "ports"
44 | runTest 0 $output spython recipe $here/testdata/Dockerfile $outfile
45 | runTest 0 $output test -f "$outfile"
46 | runTest 0 $output cat $outfile | grep "%post"
47 |
48 | # Force is false, should fail
49 | echo "#### Testing recipe json export, writing to file"
50 | runTest 0 $output spython recipe --json $here/testdata/Dockerfile $outfile
51 | runTest 0 $output spython recipe --force --json $here/testdata/Dockerfile $outfile
52 | runTest 0 $output test -f "$outfile"
53 | runTest 0 $output cat $outfile | grep "ports"
54 |
55 | echo "Finish testing basic client"
56 | rm -rf ${tmpdir}
57 |
--------------------------------------------------------------------------------
/spython/tests/test_conversion.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Copyright (C) 2017-2024 Vanessa Sochat.
4 |
5 | # This Source Code Form is subject to the terms of the
6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 |
9 | import os
10 | from glob import glob
11 |
12 |
13 | def read_file(file):
14 | with open(file) as fd:
15 | content = fd.read().strip("\n")
16 | return content
17 |
18 |
19 | def test_other_recipe_exists(test_data):
20 | # Have any example
21 | assert test_data["d2s"]
22 | assert test_data["s2d"]
23 |
24 | for _, outFile in test_data["d2s"] + test_data["s2d"]:
25 | assert os.path.exists(outFile), outFile + " is missing"
26 |
27 | dockerfiles = glob(
28 | os.path.join(os.path.dirname(test_data["s2d"][0][0]), "*.docker")
29 | )
30 | singularityfiles = glob(
31 | os.path.join(os.path.dirname(test_data["d2s"][0][0]), "*.def")
32 | )
33 | for file in dockerfiles:
34 | assert file in [out for _, out in test_data["s2d"]]
35 | for file in singularityfiles:
36 | assert file in [out for _, out in test_data["d2s"]]
37 |
38 |
39 | def test_docker2singularity(test_data, tmp_path):
40 | from spython.main.parse.parsers import DockerParser
41 | from spython.main.parse.writers import SingularityWriter
42 |
43 | for dockerfile, recipe in test_data["d2s"]:
44 | parser = DockerParser(dockerfile)
45 | writer = SingularityWriter(parser.recipe)
46 | result = read_file(recipe).strip()
47 | assert writer.convert().replace("\n", "") == result.replace("\n", "")
48 |
49 |
50 | def test_singularity2docker(test_data, tmp_path):
51 | print("Testing spython conversion from singularity2docker")
52 | from spython.main.parse.parsers import SingularityParser
53 | from spython.main.parse.writers import DockerWriter
54 |
55 | for recipe, dockerfile in test_data["s2d"]:
56 | parser = SingularityParser(recipe)
57 | writer = DockerWriter(parser.recipe)
58 | assert writer.convert() == read_file(dockerfile)
59 |
--------------------------------------------------------------------------------
/spython/tests/test_instances.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Copyright (C) 2017-2024 Vanessa Sochat.
4 |
5 | # This Source Code Form is subject to the terms of the
6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 |
9 | # name instance based on Python version in case running in parallel
10 | import sys
11 |
12 | import pytest
13 |
14 | from spython.main import Client
15 |
16 | version_string = "%s_%s_%s" % (
17 | sys.version_info[0],
18 | sys.version_info[1],
19 | sys.version_info[2],
20 | )
21 |
22 |
23 | def test_instance_class():
24 | instance = Client.instance("docker://ubuntu", start=False)
25 | assert instance.get_uri() == "instance://" + instance.name
26 | assert instance.name != ""
27 |
28 | name = "coolName"
29 | instance = Client.instance("docker://busybox:1.30.1", start=False, name=name)
30 | assert instance.get_uri() == "instance://" + instance.name
31 | assert instance.name == name
32 |
33 |
34 | def test_has_no_instances():
35 | instances = Client.instances()
36 | assert instances == []
37 |
38 |
39 | class TestInstanceFuncs:
40 | @pytest.fixture(autouse=True)
41 | def test_instance_cmds(self, docker_container):
42 | image = docker_container[1]
43 | instance_name = "instance1_" + version_string
44 | myinstance = Client.instance(image, name=instance_name)
45 | assert myinstance.get_uri().startswith("instance://")
46 |
47 | print("...Case 2: List instances")
48 | instances = Client.instances()
49 | assert len(instances) == 1
50 | instances = Client.instances(return_json=True)
51 | assert len(instances) == 1
52 | assert isinstance(instances[0], dict)
53 |
54 | print("...Case 3: Commands to instances")
55 | result = Client.execute(myinstance, ["echo", "hello"])
56 | assert result == "hello\n"
57 |
58 | print("...Case 4: Return value from instance")
59 | result = Client.execute(myinstance, "ls /", return_result=True)
60 | print(result)
61 | assert "tmp\nusr\nvar" in result["message"]
62 | assert result["return_code"] == 0
63 |
64 | print("...Case 5: Stop instances")
65 | myinstance.stop()
66 | instances = Client.instances()
67 | assert instances == []
68 | myinstance1 = Client.instance(image, name="instance1_" + version_string)
69 | myinstance2 = Client.instance(image, name="instance2_" + version_string)
70 | assert myinstance1 is not None
71 | assert myinstance2 is not None
72 | instances = Client.instances()
73 | assert len(instances) == 2
74 | myinstance1.stop()
75 | myinstance2.stop()
76 | instances = Client.instances()
77 | assert instances == []
78 |
--------------------------------------------------------------------------------
/spython/tests/test_parsers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Copyright (C) 2017-2024 Vanessa Sochat.
4 |
5 | # This Source Code Form is subject to the terms of the
6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 |
9 | import os
10 |
11 | from spython.main.parse.parsers import DockerParser, SingularityParser
12 |
13 |
14 | def test_get_parser():
15 | from spython.main.parse.parsers import get_parser
16 |
17 | parser = get_parser("docker")
18 | assert parser == DockerParser
19 |
20 | parser = get_parser("Dockerfile")
21 | assert parser == DockerParser
22 |
23 | parser = get_parser("Singularity")
24 | assert parser == SingularityParser
25 |
26 |
27 | def test_docker_parser(test_data):
28 | dockerfile = os.path.join(test_data["root"], "Dockerfile")
29 | parser = DockerParser(dockerfile)
30 |
31 | assert str(parser) == "[spython-parser][docker]"
32 | assert "spython-base" in parser.recipe
33 | recipe = parser.recipe["spython-base"]
34 |
35 | # Test all fields from recipe
36 | assert recipe.fromHeader == "python:3.5.1"
37 | assert recipe.cmd == "/code/run_uwsgi.sh"
38 | assert recipe.entrypoint is None
39 | assert recipe.workdir == "/code"
40 | assert recipe.volumes == []
41 | assert recipe.ports == ["3031"]
42 | assert recipe.files[0] == ["requirements.txt", "/tmp/requirements.txt"]
43 | assert recipe.environ == ["PYTHONUNBUFFERED=1"]
44 | assert recipe.source == dockerfile
45 |
46 |
47 | def test_singularity_parser(test_data):
48 | recipefile = os.path.join(test_data["root"], "Singularity")
49 | parser = SingularityParser(recipefile)
50 |
51 | assert str(parser) == "[spython-parser][singularity]"
52 | assert "spython-base" in parser.recipe
53 | recipe = parser.recipe["spython-base"]
54 |
55 | # Test all fields from recipe
56 | assert recipe.fromHeader == "continuumio/miniconda3"
57 | assert recipe.cmd == 'exec /opt/conda/bin/spython "$@"'
58 | assert recipe.entrypoint is None
59 | assert recipe.workdir is None
60 | assert recipe.volumes == []
61 | assert recipe.files == []
62 | assert recipe.environ == []
63 | assert recipe.source == recipefile
64 |
--------------------------------------------------------------------------------
/spython/tests/test_recipe.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Copyright (C) 2017-2024 Vanessa Sochat.
4 |
5 | # This Source Code Form is subject to the terms of the
6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 |
9 |
10 | def test_recipe_base():
11 | from spython.main.parse.recipe import Recipe
12 |
13 | recipe = Recipe()
14 | assert str(recipe) == "[spython-recipe]"
15 |
16 | attributes = [
17 | "cmd",
18 | "comments",
19 | "entrypoint",
20 | "environ",
21 | "files",
22 | "install",
23 | "labels",
24 | "ports",
25 | "test",
26 | "volumes",
27 | "workdir",
28 | ]
29 |
30 | for att in attributes:
31 | assert hasattr(recipe, att)
32 |
33 | print("Checking that empty recipe returns empty")
34 | result = recipe.json()
35 | assert not result
36 |
37 | print("Checking that non-empty recipe returns values")
38 | recipe.cmd = ["echo", "hello"]
39 | recipe.entrypoint = "/bin/bash"
40 | recipe.comments = ["This recipe is great", "Yes it is!"]
41 | recipe.environ = ["PANCAKES=WITHSYRUP"]
42 | recipe.files = [["one", "two"]]
43 | recipe.test = ["true"]
44 | recipe.install = ["apt-get update"]
45 | recipe.labels = ["Maintainer vanessasaur"]
46 | recipe.ports = ["3031"]
47 | recipe.volumes = ["/data"]
48 | recipe.workdir = "/code"
49 |
50 | result = recipe.json()
51 | for att in attributes:
52 | assert att in result
53 |
--------------------------------------------------------------------------------
/spython/tests/test_writers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Copyright (C) 2017-2024 Vanessa Sochat.
4 |
5 | # This Source Code Form is subject to the terms of the
6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 |
9 | import os
10 |
11 | from spython.main.parse.writers import DockerWriter, SingularityWriter
12 |
13 |
14 | def test_writers():
15 | from spython.main.parse.writers import get_writer
16 |
17 | writer = get_writer("docker")
18 | assert writer == DockerWriter
19 |
20 | writer = get_writer("Dockerfile")
21 | assert writer == DockerWriter
22 |
23 | writer = get_writer("Singularity")
24 | assert writer == SingularityWriter
25 |
26 |
27 | def test_docker_writer(test_data):
28 | from spython.main.parse.parsers import DockerParser
29 |
30 | dockerfile = os.path.join(test_data["root"], "Dockerfile")
31 | parser = DockerParser(dockerfile)
32 | writer = DockerWriter(parser.recipe)
33 |
34 | assert str(writer) == "[spython-writer][docker]"
35 | print(writer.convert())
36 |
37 |
38 | def test_singularity_writer(test_data):
39 | from spython.main.parse.parsers import SingularityParser
40 |
41 | recipe = os.path.join(test_data["root"], "Singularity")
42 | parser = SingularityParser(recipe)
43 | writer = SingularityWriter(parser.recipe)
44 |
45 | assert str(writer) == "[spython-writer][singularity]"
46 | print(writer.convert())
47 |
--------------------------------------------------------------------------------
/spython/tests/testdata/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.5.1
2 | ENV PYTHONUNBUFFERED 1
3 |
4 | ################################################################################
5 | # CORE
6 | # Do not modify this section
7 |
8 | RUN apt-get update && apt-get install -y \
9 | pkg-config \
10 | cmake \
11 | openssl \
12 | wget \
13 | git \
14 | vim
15 |
16 | RUN apt-get update && apt-get install -y \
17 | anacron \
18 | autoconf \
19 | automake \
20 | libarchive-dev \
21 | libtool \
22 | libopenblas-dev \
23 | libglib2.0-dev \
24 | gfortran \
25 | libxml2-dev \
26 | libxmlsec1-dev \
27 | libhdf5-dev \
28 | libgeos-dev \
29 | libsasl2-dev \
30 | libldap2-dev \
31 | squashfs-tools \
32 | build-essential
33 |
34 | # Install Singularity
35 | RUN git clone -b vault/release-2.5 https://www.github.com/sylabs/singularity.git
36 | WORKDIR singularity
37 | RUN ./autogen.sh && ./configure --prefix=/usr/local && make && make install
38 |
39 | # Install Python requirements out of /tmp so not triggered if other contents of /code change
40 | ADD requirements.txt /tmp/requirements.txt
41 | RUN pip install --upgrade pip
42 | RUN pip install -r /tmp/requirements.txt
43 |
44 | ADD . /code/
45 |
46 | ################################################################################
47 | # PLUGINS
48 | # You are free to comment out those plugins that you don't want to use
49 |
50 | # Install LDAP (uncomment if wanted)
51 | # RUN pip install python3-ldap
52 | # RUN pip install django-auth-ldap
53 |
54 | # Install Globus (uncomment if wanted)
55 | # RUN /bin/bash /code/scripts/globus/globus-install.sh
56 |
57 | # Install SAML (uncomment if wanted)
58 | # RUN pip install python3-saml
59 | # RUN pip install social-auth-core[saml]
60 |
61 | ################################################################################
62 | # BASE
63 |
64 | RUN mkdir -p /code && mkdir -p /code/images
65 | RUN mkdir -p /var/www/images && chmod -R 0755 /code/images/
66 |
67 | USER tacos
68 |
69 | WORKDIR /code
70 | RUN apt-get remove -y gfortran
71 |
72 | RUN apt-get autoremove -y
73 | RUN apt-get clean
74 | RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
75 |
76 | # Install crontab to setup job
77 | RUN echo "0 0 * * * /usr/bin/python /code/manage.py generate_tree" >> /code/cronjob
78 | RUN crontab /code/cronjob
79 | RUN rm /code/cronjob
80 |
81 | # Create hashed temporary upload locations
82 | RUN mkdir -p /var/www/images/_upload/{0..9} && chmod 777 -R /var/www/images/_upload
83 |
84 | CMD /code/run_uwsgi.sh
85 |
86 | EXPOSE 3031
87 |
--------------------------------------------------------------------------------
/spython/tests/testdata/README.md:
--------------------------------------------------------------------------------
1 | # Test Data
2 |
3 | This folder contains test data for Singularity Python.
4 |
5 | - [Singularity](Singularity) and [Docker](Docker) are generic recipes used to run the tests in [one folder up](../). They are not inended to be converted between one another.
6 | - [singularity2docker](singularity2docker) is a folder of docker recipes (`*.docker`) and Singularity recipes (`*.def`) that are tested for conversion *from* Singularity to Docker.
7 | - [docker2singularity](docker2singularity) is a folder of Singularity recipes (`*.def`) and docker recipes (`*.docker`) that are tested for conversion *from* Docker to Singularity.
8 |
9 | To add a new pair of recipes to either folder, simply write a .def and .docker file with the same name. They will be tested by [test_conversion.py](../test_conversion.py).
10 |
--------------------------------------------------------------------------------
/spython/tests/testdata/Singularity:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: continuumio/miniconda3
3 |
4 | %runscript
5 | exec /opt/conda/bin/spython "$@"
6 |
7 | %labels
8 | maintainer vsochat@stanford.edu
9 |
10 | %post
11 | apt-get update && apt-get install -y git
12 |
13 | # Dependencies
14 | cd /opt
15 | git clone https://www.github.com/singularityhub/singularity-cli
16 | cd singularity-cli
17 | /opt/conda/bin/pip install setuptools
18 | /opt/conda/bin/python setup.py install
19 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/add.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %files
6 | . /opt
7 | %runscript
8 | exec /bin/bash "$@"
9 | %startscript
10 | exec /bin/bash "$@"
11 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/add.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | ADD . /opt
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/argsub.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: nvidia/cuda:11.1.1-cudnn8-devel-ubuntu20.04
3 | Stage: spython-base
4 |
5 | %files
6 | ./requirements.txt /workspace
7 | %labels
8 | maintainer="Dong Wang"
9 | %post
10 | CUDA_VERSION=11.1.1
11 | OS_VERSION=20.04
12 |
13 |
14 |
15 |
16 | PATH="/root/miniconda3/bin:${PATH}"
17 | PATH="/root/miniconda3/bin:${PATH}"
18 | DEBIAN_FRONTEND=noninteractive
19 |
20 | SHELL ["/bin/bash", "-c"]
21 |
22 | apt-get update && apt-get upgrade -y &&\
23 | apt-get install -y wget python3-pip
24 |
25 | python3 -m pip install --upgrade pip
26 |
27 | mkdir -p /workspace
28 | cd /workspace
29 | python3 -m pip install -r /workspace/requirements.txt and &&\
30 | rm /workspace/requirements.txt
31 |
32 | %environment
33 | export PATH="/root/miniconda3/bin:${PATH}"
34 | %runscript
35 | cd /workspace
36 | exec /bin/bash /bin/bash "$@"
37 | %startscript
38 | cd /workspace
39 | exec /bin/bash /bin/bash "$@"
40 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/argsub.docker:
--------------------------------------------------------------------------------
1 | ARG CUDA_VERSION=11.1.1
2 | ARG OS_VERSION=20.04
3 |
4 | FROM nvidia/cuda:${CUDA_VERSION}-cudnn8-devel-ubuntu${OS_VERSION}
5 |
6 | LABEL maintainer="Dong Wang"
7 |
8 |
9 | ENV PATH="/root/miniconda3/bin:${PATH}"
10 | ARG PATH="/root/miniconda3/bin:${PATH}"
11 | ARG DEBIAN_FRONTEND=noninteractive
12 |
13 | SHELL ["/bin/bash", "-c"]
14 |
15 | RUN apt-get update && apt-get upgrade -y &&\
16 | apt-get install -y wget python3-pip
17 |
18 | RUN python3 -m pip install --upgrade pip
19 |
20 | WORKDIR /workspace
21 | ADD ./requirements.txt /workspace
22 | RUN python3 -m pip install -r /workspace/requirements.txt and &&\
23 | rm /workspace/requirements.txt
24 |
25 | CMD ["/bin/bash"]
26 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/cmd.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %runscript
6 | exec /bin/bash echo hello "$@"
7 | %startscript
8 | exec /bin/bash echo hello "$@"
9 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/cmd.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | CMD ["echo", "hello"]
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/comments.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %post
6 |
7 | # This is a really important line
8 | cp /bin/echo /opt/echo
9 |
10 | # I'm sure you agree with me?
11 | %runscript
12 | exec /bin/bash "$@"
13 | %startscript
14 | exec /bin/bash "$@"
15 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/comments.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 |
3 | # This is a really important line
4 | RUN cp /bin/echo /opt/echo
5 |
6 | # I'm sure you agree with me?
7 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/copy.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %files
6 | . /opt
7 | %runscript
8 | exec /bin/bash "$@"
9 | %startscript
10 | exec /bin/bash "$@"
11 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/copy.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | COPY . /opt
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/entrypoint-cmd.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %runscript
6 | exec python /code/script.py "$@"
7 | %startscript
8 | exec python /code/script.py "$@"
9 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/entrypoint-cmd.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | CMD ["/code/script.py"]
3 | ENTRYPOINT ["python"]
4 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/entrypoint.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %runscript
6 | exec /bin/bash run_uwsgi.sh "$@"
7 | %startscript
8 | exec /bin/bash run_uwsgi.sh "$@"
9 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/entrypoint.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | ENTRYPOINT /bin/bash run_uwsgi.sh
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/expose.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %post
6 | # EXPOSE 3031
7 | # EXPOSE 9000
8 | %runscript
9 | exec /bin/bash "$@"
10 | %startscript
11 | exec /bin/bash "$@"
12 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/expose.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | EXPOSE 3031
3 | EXPOSE 9000
4 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/from.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %runscript
6 | exec /bin/bash "$@"
7 | %startscript
8 | exec /bin/bash "$@"
9 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/from.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/healthcheck.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %runscript
6 | exec /bin/bash "$@"
7 | %startscript
8 | exec /bin/bash "$@"
9 | %test
10 | true
11 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/healthcheck.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | HEALTHCHECK true
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/label.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %labels
6 | maintainer dinosaur
7 | %runscript
8 | exec /bin/bash "$@"
9 | %startscript
10 | exec /bin/bash "$@"
11 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/label.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | LABEL maintainer dinosaur
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/multiple-lines.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %post
6 |
7 | apt-get update && \
8 | apt-get install -y git \
9 | wget \
10 | curl \
11 | squashfs-tools
12 | %runscript
13 | exec /bin/bash "$@"
14 | %startscript
15 | exec /bin/bash "$@"
16 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/multiple-lines.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 |
3 | RUN apt-get update && \
4 | apt-get install -y git \
5 | wget \
6 | curl \
7 | squashfs-tools
8 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/multistage.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: golang:1.12.3-alpine3.9
3 | Stage: devel
4 |
5 | %post
6 | export PATH="/go/bin:/usr/local/go/bin:$PATH"
7 | export HOME="/root"
8 | cd /root
9 | touch hello
10 |
11 | Bootstrap: docker
12 | From: alpine:3.9
13 | Stage: final
14 |
15 | %files from devel
16 | /root/hello /bin/hello
17 | %runscript
18 | exec /bin/bash "$@"
19 | %startscript
20 | exec /bin/bash "$@"
21 |
22 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/multistage.docker:
--------------------------------------------------------------------------------
1 | FROM golang:1.12.3-alpine3.9 AS devel
2 | RUN export PATH="/go/bin:/usr/local/go/bin:$PATH"
3 | RUN export HOME="/root"
4 | RUN cd /root
5 | RUN touch hello
6 | FROM alpine:3.9 AS final
7 | COPY --from=devel /root/hello /bin/hello
8 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/user.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %post
6 | echo "cloud"
7 | su - rainman # USER rainman
8 | echo "makeitrain"
9 | su - root # USER root
10 | %runscript
11 | exec /bin/bash "$@"
12 | %startscript
13 | exec /bin/bash "$@"
14 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/user.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | RUN echo "cloud"
3 | USER rainman
4 | RUN echo "makeitrain"
5 | USER root
6 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/workdir.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | Stage: spython-base
4 |
5 | %post
6 | mkdir -p /code
7 | cd /code
8 | %runscript
9 | cd /code
10 | exec /bin/bash "$@"
11 | %startscript
12 | cd /code
13 | exec /bin/bash "$@"
14 |
--------------------------------------------------------------------------------
/spython/tests/testdata/docker2singularity/workdir.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest
2 | WORKDIR /code
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/files.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | %files
4 | file.txt /opt/file.txt
5 | /path/to/thing /opt/thing
6 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/files.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest AS spython-base
2 | ADD file.txt /opt/file.txt
3 | ADD /path/to/thing /opt/thing
4 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/from.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/from.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest AS spython-base
2 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/labels.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | %labels
4 | Maintainer dinosaur
5 | Version 1.0.0
6 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/labels.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest AS spython-base
2 | LABEL Maintainer dinosaur
3 | LABEL Version 1.0.0
4 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/multiple-lines.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | %post
4 |
5 | apt-get update && \
6 | apt-get install -y git \
7 | wget \
8 | curl \
9 | squashfs-tools
10 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/multiple-lines.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest AS spython-base
2 | RUN apt-get update && \
3 | apt-get install -y git \
4 | wget \
5 | curl \
6 | squashfs-tools
7 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/multistage.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: golang:1.12.3-alpine3.9
3 | Stage: devel
4 |
5 | %post
6 | # prep environment
7 | export PATH="/go/bin:/usr/local/go/bin:$PATH"
8 | export HOME="/root"
9 | cd /root
10 | touch hello
11 |
12 | # Install binary into final image
13 | Bootstrap: docker
14 | From: alpine:3.9
15 | Stage: final
16 |
17 | # install binary from stage one
18 | %files from devel
19 | /root/hello /bin/hello
20 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/multistage.docker:
--------------------------------------------------------------------------------
1 | FROM golang:1.12.3-alpine3.9 AS devel
2 | RUN export PATH="/go/bin:/usr/local/go/bin:$PATH"
3 | RUN export HOME="/root"
4 | RUN cd /root
5 | RUN touch hello
6 | FROM alpine:3.9 AS final
7 | COPY --from=devel /root/hello /bin/hello
8 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/post.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | %post
4 | apt-get update
5 | apt-get install -y git \
6 | wget
7 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/post.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest AS spython-base
2 | RUN apt-get update
3 | RUN apt-get install -y git \
4 | wget
5 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/runscript.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | %runscript
4 | exec /bin/bash echo hello "$@"
5 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/runscript.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest AS spython-base
2 | CMD exec /bin/bash echo hello "$@"
3 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/test.def:
--------------------------------------------------------------------------------
1 | Bootstrap: docker
2 | From: busybox:latest
3 | %test
4 | true
5 |
--------------------------------------------------------------------------------
/spython/tests/testdata/singularity2docker/test.docker:
--------------------------------------------------------------------------------
1 | FROM busybox:latest AS spython-base
2 | RUN echo "true" >> /tests.sh
3 | RUN chmod u+x /tests.sh
4 | HEALTHCHECK /bin/bash /tests.sh
5 |
--------------------------------------------------------------------------------
/spython/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from .fileio import mkdir_p, read_file, read_json, write_file, write_json
2 | from .misc import ScopedEnvVar
3 | from .terminal import (
4 | check_install,
5 | format_container_name,
6 | get_installdir,
7 | get_singularity_version,
8 | get_userhome,
9 | get_username,
10 | remove_uri,
11 | run_command,
12 | split_uri,
13 | stream_command,
14 | )
15 |
--------------------------------------------------------------------------------
/spython/utils/fileio.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | import errno
8 | import json
9 | import os
10 | import sys
11 |
12 | from spython.logger import bot
13 |
14 | ################################################################################
15 | ## FOLDER OPERATIONS ###########################################################
16 | ################################################################################
17 |
18 |
19 | def mkdir_p(path):
20 | """mkdir_p attempts to get the same functionality as mkdir -p
21 | :param path: the path to create.
22 | """
23 | try:
24 | os.makedirs(path)
25 | except OSError as e:
26 | if e.errno == errno.EEXIST and os.path.isdir(path):
27 | pass
28 | else:
29 | bot.error("Error creating path %s, exiting." % path)
30 | sys.exit(1)
31 |
32 |
33 | ################################################################################
34 | ## FILE OPERATIONS #############################################################
35 | ################################################################################
36 |
37 |
38 | def write_file(filename, content, mode="w"):
39 | """write_file will open a file, "filename" and write content, "content"
40 | and properly close the file
41 | """
42 | with open(filename, mode) as filey:
43 | filey.writelines(content)
44 | return filename
45 |
46 |
47 | def write_json(json_obj, filename, mode="w", print_pretty=True):
48 | """
49 | write_json will (optionally,pretty print) a json object to file
50 | :param json_obj: the dict to print to json
51 | :param filename: the output file to write to
52 | :param pretty_print: if True, will use nicer formatting
53 | """
54 | with open(filename, mode) as filey:
55 | if print_pretty:
56 | filey.writelines(json.dumps(json_obj, indent=4, separators=(",", ": ")))
57 | else:
58 | filey.writelines(json.dumps(json_obj))
59 | return filename
60 |
61 |
62 | def read_file(filename, mode="r", readlines=True):
63 | """
64 | write_file will open a file, "filename" and write content, "content"
65 | and properly close the file
66 | """
67 | with open(filename, mode) as filey:
68 | if readlines:
69 | content = filey.readlines()
70 | else:
71 | content = filey.read()
72 | return content
73 |
74 |
75 | def read_json(filename, mode="r"):
76 | """
77 | read_json reads in a json file and returns
78 | the data structure as dict.
79 | """
80 | with open(filename, mode) as filey:
81 | data = json.load(filey)
82 | return data
83 |
--------------------------------------------------------------------------------
/spython/utils/misc.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | import os
8 |
9 |
10 | def setEnvVar(name, value):
11 | """Set or unset an environment variable
12 |
13 | name -- Name of the variable to set
14 | value -- Value to use or None to clear
15 | """
16 | if value is None:
17 | if name in os.environ:
18 | del os.environ[name]
19 | else:
20 | os.environ[name] = value
21 |
22 |
23 | class ScopedEnvVar:
24 | """Temporarily change an environment variable
25 |
26 | Usage:
27 | with ScopedEnvVar("FOO", "bar"):
28 | print(os.environ["FOO"]) # "bar"
29 | print(os.environ["FOO"]) #
30 | """
31 |
32 | def __init__(self, name, value):
33 | """Create the scoped environment variable object
34 |
35 | name -- Name of the variable to set
36 | value -- Value to use or None to clear
37 | """
38 | self.name = name
39 | self.value = value
40 | self.oldValue = None
41 |
42 | def __enter__(self):
43 | self.oldValue = os.environ.get(self.name)
44 | setEnvVar(self.name, self.value)
45 | return self
46 |
47 | def __exit__(self, ex_type, ex_value, traceback):
48 | setEnvVar(self.name, self.oldValue)
49 |
--------------------------------------------------------------------------------
/spython/version.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2017-2024 Vanessa Sochat.
2 |
3 | # This Source Code Form is subject to the terms of the
4 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
5 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 |
8 | __version__ = "0.3.14"
9 | AUTHOR = "Vanessa Sochat"
10 | AUTHOR_EMAIL = "vsoch@users.noreply.github.com"
11 | NAME = "spython"
12 | PACKAGE_URL = "https://github.com/singularityhub/singularity-cli"
13 | KEYWORDS = "singularity python client (spython)"
14 | DESCRIPTION = "Command line python tool for working with singularity."
15 | LICENSE = "LICENSE"
16 |
17 | INSTALL_REQUIRES = ()
18 | TESTS_REQUIRES = (("pytest", {"min_version": "4.6.2"}),)
19 |
--------------------------------------------------------------------------------