diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..217ed8e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+docs/syntax-highlighting.css linguist-generated
+docs/theme.css linguist-generated
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..f72beb8
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+ko_fi: cssnr
diff --git a/.github/disabled/publish.yaml b/.github/disabled/publish.yaml
deleted file mode 100644
index 0eba812..0000000
--- a/.github/disabled/publish.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: "GitHub Pages"
-
-on:
- release:
- types: [published]
-# workflow_dispatch:
-# environment:
-# type: choice
-# description: Choose Environment
-# options:
-# - testpypi
-# - pypi
- push:
-
-env:
- ENVIRONMENT: "testpypi"
- REPOSITORY: "https://test.pypi.org/legacy/"
-
-jobs:
- publish:
- name: "Publish"
- runs-on: ubuntu-latest
- timeout-minutes: 5
- if: github.event_name == 'release' && !github.event.release.prerelease
-
- permissions:
- id-token: write
-
- environment:
- name: ${{ env.ENVIRONMENT }}
- url: https://pypi.org/project/vultr-python/
-
- steps:
- - name: "PyPi Publish"
- uses: pypa/gh-action-pypi-publish@release/v1
- with:
- repository-url: ${{ env.REPOSITORY }}
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index e61c27d..92a7c85 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -1,11 +1,16 @@
-name: "Build"
+name: "Build Package"
on:
- workflow_dispatch:
- release:
- types: [published]
- pull_request:
- branches: ["master"]
+ workflow_call:
+ inputs:
+ name:
+ description: "Artifact Name"
+ type: string
+ required: true
+ path:
+ description: "Build Path"
+ type: string
+ default: dist
jobs:
build:
@@ -15,21 +20,60 @@ jobs:
steps:
- name: "Checkout"
- uses: actions/checkout@v3
+ uses: actions/checkout@v5
- - name: "Setup Python"
- uses: actions/setup-python@v4
+ - name: "Debug event.json"
+ continue-on-error: true
+ run: cat "${GITHUB_EVENT_PATH}"
+
+ - name: "Debug CTX github"
+ continue-on-error: true
+ env:
+ GITHUB_CTX: ${{ toJSON(github) }}
+ run: echo "$GITHUB_CTX"
+
+ #- name: "Debug Environment"
+ # continue-on-error: true
+ # run: env
+
+ - name: "Setup Python 3.13"
+ uses: actions/setup-python@v6
with:
- python-version: "3.11"
+ python-version: "3.13"
+ cache: "pip"
- - name: "Build"
+ - name: "Install"
run: |
python -m pip install -U pip
- python -m pip install -U build
+ python -m pip install --group dev
+
+ - name: "Build"
+ run: |
python -m build
- - name: "Upload Artifact"
- uses: actions/upload-artifact@v3
+ - name: "List Artifacts"
+ env:
+ path: ${{ inputs.path }}
+ run: |
+ echo "::group::ls"
+ ls -lAhR "${path}"
+ echo "::endgroup::"
+ echo "::group::tree"
+ tree "${path}"
+ echo "::endgroup::"
+ results="$(tree "${path}")"
+ markdown='Artifacts:\n```text\n'"${results}"'\n```'
+ echo -e "${markdown}" >> $GITHUB_STEP_SUMMARY
+
+ - name: "Upload to Actions"
+ uses: actions/upload-artifact@v5
+ with:
+ name: ${{ inputs.name }}
+ path: ${{ inputs.path }}
+
+ - name: "Send Failure Notification"
+ if: ${{ failure() }}
+ uses: sarisia/actions-status-discord@v1
with:
- name: vultr-python
- path: dist/
+ webhook: ${{ secrets.DISCORD_WEBHOOK }}
+ description: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 13916ff..21272c5 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -1,12 +1,20 @@
-name: "GitHub Pages"
+name: "Build Docs"
on:
- push:
- branches: ["master"]
- workflow_dispatch:
- repository_dispatch:
- types:
- - webhook
+ workflow_call:
+ inputs:
+ name:
+ description: "Artifact Name"
+ type: string
+ default: github-pages
+ path:
+ description: "Build Path"
+ type: string
+ default: site
+
+env:
+ logo: "https://raw.githubusercontent.com/smashedr/repo-images/refs/heads/master/vultr-python/logo128.png"
+ link: ${{ github.event.repository.html_url }}
jobs:
build:
@@ -16,51 +24,75 @@ jobs:
steps:
- name: "Checkout"
- uses: actions/checkout@v3
+ uses: actions/checkout@v5
+
+ - name: "Debug CTX github"
+ continue-on-error: true
+ env:
+ GITHUB_CTX: ${{ toJSON(github) }}
+ run: echo "$GITHUB_CTX"
+
+ - name: "Debug"
+ continue-on-error: true
+ run: |
+ echo "logo: ${{ env.logo }}"
+ echo "link: ${{ env.link }}"
- - name: "Setup Python"
- uses: actions/setup-python@v4
+ - name: "Setup Python 3.13"
+ uses: actions/setup-python@v6
with:
- python-version: "3.11"
+ python-version: "3.13"
+ cache: "pip"
- name: "Build Docs"
run: |
python -m pip install -U pip
python -m pip install -U pdoc
- python -m pip install -Ur requirements.txt
- python -m pdoc -o _site vultr.py
+ python -m pip install -e .
+ python -m pdoc -t docs -o "${{ inputs.path }}" \
+ --favicon "${{ env.logo }}" \
+ --logo "${{ env.logo }}" \
+ --logo-link "${{ env.link }}" \
+ vultr
- name: "Update Permissions"
run: |
- chmod -c -R +rX "_site/" | while read line; do
- echo "::warning title=Invalid file permissions automatically fixed::$line"
+ chmod -c -R +rX "${{ inputs.path }}" | while read name; do
+ echo "::notice::Fixed invalid file permissions: ${name}"
done
+ - name: "List Artifacts"
+ env:
+ path: ${{ inputs.path }}
+ run: |
+ echo "::group::ls"
+ ls -lAhR "${path}"
+ echo "::endgroup::"
+ echo "::group::tree"
+ tree "${path}"
+ echo "::endgroup::"
+ results="$(tree "${path}")"
+ markdown='Artifacts:\n```text\n'"${results}"'\n```'
+ echo -e "${markdown}" >> $GITHUB_STEP_SUMMARY
+
- name: "Upload Pages Artifact"
- uses: actions/upload-pages-artifact@v2
+ if: ${{ inputs.name == 'github-pages' }}
+ uses: actions/upload-pages-artifact@v4
with:
- path: _site/
+ path: ${{ inputs.path }}
- deploy:
- name: "Deploy"
- runs-on: ubuntu-latest
- timeout-minutes: 5
- needs: build
-
- permissions:
- pages: write
- id-token: write
-
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
-
- steps:
- - name: "Configure Pages"
- uses: actions/configure-pages@v3
+ - name: "Upload Artifact"
+ if: ${{ inputs.name != 'github-pages' }}
+ uses: actions/upload-artifact@v5
+ with:
+ name: ${{ inputs.name }}
+ path: ${{ inputs.path }}
+ if-no-files-found: "error"
+ include-hidden-files: true
- - name: "Deploy Pages"
- id: deployment
- uses: actions/deploy-pages@v2
+ - name: "Send Failure Notification"
+ if: ${{ failure() }}
+ uses: sarisia/actions-status-discord@v1
with:
- artifact_name: github-pages
+ webhook: ${{ secrets.DISCORD_WEBHOOK }}
+ description: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
new file mode 100644
index 0000000..7dfc8cd
--- /dev/null
+++ b/.github/workflows/lint.yaml
@@ -0,0 +1,167 @@
+name: "Lint"
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [master]
+ paths-ignore:
+ - ".gitignore"
+ - ".prettierignore"
+ - "MANIFEST.in"
+ - "pyproject.toml"
+ pull_request:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ lint:
+ name: "Lint"
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+
+ permissions:
+ contents: read
+
+ steps:
+ - name: "Checkout"
+ uses: actions/checkout@v5
+
+ - name: "Debug event.json"
+ continue-on-error: true
+ run: cat "${GITHUB_EVENT_PATH}"
+
+ - name: "Debug CTX github"
+ continue-on-error: true
+ env:
+ GITHUB_CTX: ${{ toJSON(github) }}
+ run: echo "$GITHUB_CTX"
+
+ - name: "Debug Environment"
+ continue-on-error: true
+ run: env
+
+ #- name: "Check Changed Files"
+ # id: changed
+ # uses: tj-actions/changed-files@v47
+ # with:
+ # files: |
+ # src/**
+ # tests/**
+ # pyproject.toml
+
+ - name: "Setup Python 3.13"
+ uses: actions/setup-python@v6
+ with:
+ python-version: "3.13"
+ cache: "pip"
+
+ - name: "Install"
+ id: install
+ run: |
+ python -m pip install -U pip
+ python -m pip install --group dev
+ python -m pip install -e .
+ python -m pip list
+
+ - name: "Debug"
+ continue-on-error: true
+ run: |
+ python -V
+ which python
+ echo "::group::pip list"
+ python -m pip list
+ echo "::endgroup::"
+
+ - name: "astral-sh/ruff"
+ if: ${{ !cancelled() }}
+ uses: astral-sh/ruff-action@v3
+ with:
+ version: latest
+
+ - name: "astral-sh/ty"
+ if: ${{ !cancelled() }}
+ run: |
+ ty check --python "$(which python)" -v
+
+ - name: "psf/black"
+ if: ${{ !cancelled() }}
+ uses: psf/black@stable
+
+ - name: "isort"
+ if: ${{ !cancelled() }}
+ uses: isort/isort-action@v1
+
+ - name: "mypy"
+ if: ${{ !cancelled() }}
+ run: |
+ mypy src
+
+ - name: "bandit"
+ if: ${{ !cancelled() }}
+ run: |
+ bandit -c pyproject.toml -r src
+
+ - name: "prettier"
+ if: ${{ !cancelled() }}
+ run: |
+ echo "::group::Install"
+ npm install prettier
+ echo "::endgroup::"
+ npx prettier --check .
+
+ - name: "yamllint"
+ if: ${{ !cancelled() }}
+ env:
+ CONFIG: "{extends: relaxed, ignore: [node_modules/], rules: {line-length: {max: 119}}}"
+ run: |
+ echo "::group::List Files"
+ yamllint -d '${{ env.CONFIG }}' --list-files .
+ echo "::endgroup::"
+ yamllint -d '${{ env.CONFIG }}' .
+
+ - name: "actionlint"
+ if: ${{ !cancelled() }}
+ run: |
+ echo "::group::Download"
+ loc=$(curl -sI https://github.com/rhysd/actionlint/releases/latest | grep -i '^location:')
+ echo "loc: ${loc}"
+ tag=$(echo "${loc}" | sed -E 's|.*/tag/v?(.*)|\1|' | tr -d '\t\r\n')
+ echo "tag: ${tag}"
+ url="https://github.com/rhysd/actionlint/releases/latest/download/actionlint_${tag}_linux_amd64.tar.gz"
+ echo "url: ${url}"
+ curl -sL "${url}" | tar xz -C "${RUNNER_TEMP}" actionlint
+ file "${RUNNER_TEMP}/actionlint"
+ "${RUNNER_TEMP}/actionlint" --version
+ echo "::endgroup::"
+ "${RUNNER_TEMP}/actionlint" -color -verbose -shellcheck= -pyflakes=
+
+ #- name: "pytest"
+ # if: ${{ !cancelled() }}
+ # id: coverage
+ # run: |
+ # coverage run -m pytest
+ # coverage xml
+ # coverage report -m
+
+ #- name: "codecov"
+ # if: ${{ !cancelled() && steps.coverage.outcome == 'success' }}
+ # uses: codecov/codecov-action@v5
+ # with:
+ # token: ${{ secrets.CODECOV_TOKEN }}
+
+ #- name: "hadolint"
+ # if: ${{ !cancelled() }}
+ # uses: hadolint/hadolint-action@v3.3.0
+ # with:
+ # dockerfile: Dockerfile
+
+ #- name: "Vale"
+ # if: ${{ !cancelled() }}
+ # uses: errata-ai/vale-action@v2.1.1
+
+ #- name: "SonarQube"
+ # uses: SonarSource/sonarqube-scan-action@v4
+ # env:
+ # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/pages.yaml b/.github/workflows/pages.yaml
new file mode 100644
index 0000000..be6a19f
--- /dev/null
+++ b/.github/workflows/pages.yaml
@@ -0,0 +1,46 @@
+name: "GitHub Pages"
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [master]
+ paths:
+ - ".github/workflows/docs.yaml"
+ - ".github/workflows/pages.yaml"
+ - "docs/**"
+ - "src/**"
+
+jobs:
+ build:
+ name: "Build"
+ uses: ./.github/workflows/docs.yaml
+ secrets: inherit
+ permissions:
+ contents: read
+
+ deploy:
+ name: "Deploy"
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ needs: build
+
+ permissions:
+ pages: write
+ id-token: write
+
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: "Deploy Pages"
+ id: deployment
+ uses: actions/deploy-pages@v4
+
+ - name: "Send Notification"
+ if: ${{ !cancelled() }}
+ continue-on-error: true
+ uses: sarisia/actions-status-discord@v1
+ with:
+ webhook: ${{ secrets.DISCORD_WEBHOOK }}
+ description: ${{ steps.deployment.outputs.page_url }}
diff --git a/.github/workflows/preview.yaml b/.github/workflows/preview.yaml
new file mode 100644
index 0000000..465f3f1
--- /dev/null
+++ b/.github/workflows/preview.yaml
@@ -0,0 +1,43 @@
+name: "Preview"
+
+on:
+ workflow_dispatch:
+ push:
+ branches-ignore: [master]
+ paths:
+ - ".github/workflows/docs.yaml"
+ - ".github/workflows/preview.yaml"
+ - "docs/**"
+ - "src/**"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: "Build"
+ if: ${{ !contains(github.event.head_commit.message, '#nopreview') }}
+ uses: ./.github/workflows/docs.yaml
+ secrets: inherit
+ permissions:
+ contents: read
+ with:
+ name: artifact
+
+ deploy:
+ name: "Deploy"
+ uses: cssnr/workflows/.github/workflows/deploy-static.yaml@master
+ needs: build
+ permissions:
+ contents: read
+ with:
+ name: "preview"
+ url: "https://dev-static.cssnr.com/"
+ robots: true
+ secrets:
+ host: ${{ secrets.DEV_DEPLOY_HOST }}
+ port: ${{ secrets.DEV_DEPLOY_PORT }}
+ user: ${{ secrets.DEV_DEPLOY_USER }}
+ pass: ${{ secrets.DEV_DEPLOY_PASS }}
+ webhook: ${{ secrets.DISCORD_WEBHOOK }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
new file mode 100644
index 0000000..c575493
--- /dev/null
+++ b/.github/workflows/release.yaml
@@ -0,0 +1,168 @@
+name: "Release"
+
+on:
+ release:
+ types: [published]
+
+env:
+ url: "https://${{ github.event.release.prerelease && 'test.' || '' }}pypi.org/p/vultr-python"
+ repository-url: "https://${{ github.event.release.prerelease && 'test' || 'upload' }}.pypi.org/legacy/"
+
+jobs:
+ build:
+ name: "Build"
+ uses: ./.github/workflows/build.yaml
+ secrets: inherit
+ with:
+ name: "release"
+
+ publish:
+ name: "Publish"
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ needs: [build]
+
+ permissions:
+ contents: write
+ id-token: write
+
+ environment:
+ name: ${{ github.event.release.prerelease && 'test' || 'pypi' }}
+ url: ${{ env.url }}
+
+ steps:
+ - name: "Debug event.json"
+ continue-on-error: true
+ run: cat "${GITHUB_EVENT_PATH}"
+
+ - name: "Debug CTX github"
+ continue-on-error: true
+ env:
+ GITHUB_CTX: ${{ toJSON(github) }}
+ run: echo "$GITHUB_CTX"
+
+ #- name: "Debug Environment"
+ # continue-on-error: true
+ # run: env
+
+ - name: "Debug"
+ run: |
+ echo "github.ref_name: ${{ github.ref_name }}"
+ echo "name: ${{ github.event.release.prerelease && 'test' || 'pypi' }}"
+ echo "env.url: ${{ env.url }}"
+ echo "env.repository-url: ${{ env.repository-url }}"
+
+ - name: "Download Artifact"
+ uses: actions/download-artifact@v6
+ with:
+ name: "release"
+ path: "dist"
+
+ - name: "Verify Artifacts"
+ env:
+ path: "dist"
+ tag: ${{ github.ref_name }}
+ run: |
+ ls -lAh ${path}/*
+ results="$(ls -lAh ${path}/* | awk '{print $9" - "$5}')"
+ markdown='Artifacts:\n```text\n'"${results}"'\n```'
+ echo -e "${markdown}" >> $GITHUB_STEP_SUMMARY
+ ls ${path} | grep -- "-${tag}-" > /dev/null
+
+ - name: "Publish to PyPI"
+ id: publish
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ verbose: true
+ repository-url: ${{ env.repository-url }}
+ #attestations: false
+
+ - name: "Summary"
+ if: ${{ always() }}
+ env:
+ tag: ${{ github.ref_name }}
+ url: ${{ env.url }}
+ repository-url: ${{ env.repository-url }}
+ run: |
+ if [ "${{ steps.publish.outcome }}" == "success" ];then
+ markdown="🎉 Published Version: \`${tag}\`\n\n[${url}](${url})\n\n"
+ else
+ markdown="⛔ Error Publishing Version: \`${tag}\`\n\n"
+ fi
+ markdown+="Repository URL: \`${repository-url}\`"
+ echo -e "${markdown}" >> $GITHUB_STEP_SUMMARY
+
+ - name: "Send Notification"
+ if: ${{ !cancelled() }}
+ uses: sarisia/actions-status-discord@v1
+ with:
+ webhook: ${{ secrets.DISCORD_WEBHOOK }}
+ description: |
+ ${{ env.url }}
+ ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}
+
+ release:
+ name: "Release"
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ needs: [publish]
+
+ steps:
+ - name: "Debug"
+ continue-on-error: true
+ run: |
+ echo "github.ref_name: ${{ github.ref_name }}"
+ echo "github.event.release.prerelease: ${{ github.event.release.prerelease }}"
+
+ - name: "Debug event.json"
+ continue-on-error: true
+ run: cat "${GITHUB_EVENT_PATH}"
+
+ - name: "Debug CTX github"
+ continue-on-error: true
+ env:
+ GITHUB_CTX: ${{ toJSON(github) }}
+ run: echo "$GITHUB_CTX"
+
+ #- name: "Debug Environment"
+ # continue-on-error: true
+ # run: env
+
+ #- name: "Update Latest Tag"
+ # if: ${{ !github.event.release.prerelease }}
+ # uses: cssnr/update-version-tags-action@latest
+ # continue-on-error: true
+ # with:
+ # major: false
+ # minor: false
+ # tags: latest
+ # #tag: ${{ github.ref_name }}
+
+ - name: "Download Artifact"
+ uses: actions/download-artifact@v6
+ with:
+ name: "release"
+ path: "dist"
+
+ - name: "List Artifacts"
+ run: |
+ ls -lAh dist
+ ls dist | grep -- "-${{ github.ref_name }}-" > /dev/null
+
+ - name: "Upload Release"
+ uses: cssnr/upload-release-action@latest
+ with:
+ globs: dist/*
+
+ - name: "Update Release Notes Action"
+ continue-on-error: true
+ uses: smashedr/update-release-notes-action@master
+ with:
+ location: tail
+
+ - name: "Send Failure Notification"
+ if: ${{ failure() }}
+ uses: sarisia/actions-status-discord@v1
+ with:
+ webhook: ${{ secrets.DISCORD_WEBHOOK }}
+ description: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}
diff --git a/.gitignore b/.gitignore
index 19197fa..08b9fdc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,18 @@
+# Generic
.idea/
*.iml
.vscode/
-dist/
-_site/
+.venv/
venv/
-**/__pycache__/
+__pycache__/
*.egg-info/
+build/
+dist/
+*.log
+*.pyc
.coverage
+coverage.xml
+# Zensical
+site/
+.cache/
+# App
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..5b4267c
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,16 @@
+# IDE
+.idea/
+.vscode/
+
+# Build
+dist/
+node_modules/
+
+# Tools
+.ruff_cache/
+.mypy_cache/
+.pytest_cache/
+
+# Files
+package-lock.json
+*.html
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..4541eb0
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,20 @@
+{
+ "trailingComma": "es5",
+ "semi": false,
+ "singleQuote": true,
+ "printWidth": 90,
+ "overrides": [
+ {
+ "files": ["**/*.html", "**/*.yaml", "**/*.yml"],
+ "options": {
+ "singleQuote": false
+ }
+ },
+ {
+ "files": ["**/*.js", "**/*.mjs", "**/*.css", "**/*.scss"],
+ "options": {
+ "tabWidth": 4
+ }
+ }
+ ]
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ffe8abf
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 CSSNR
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 2901fc9..198deaf 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,61 @@
-[](https://discord.gg/wXy6m2X8wY)
+[](https://pypi.org/project/vultr-python/)
+[](https://github.com/cssnr/vultr-python?tab=readme-ov-file#readme)
+[](https://pypistats.org/packages/vultr-python)
+[](https://clickpy.clickhouse.com/dashboard/vultr-python)
[](https://app.codacy.com/gh/cssnr/vultr-python/dashboard)
-[](https://pypi.org/project/vultr-python/)
-[](https://www.vultr.com/?ref=6905748)
+[](https://sonarcloud.io/summary/new_code?id=cssnr_vultr-python)
+[](https://github.com/cssnr/vultr-python/actions/workflows/lint.yaml)
+[](https://cssnr.github.io/vultr-python)
+[](https://github.com/cssnr/vultr-python/graphs/commit-activity)
+[](https://github.com/cssnr/vultr-python)
+[](https://github.com/cssnr/vultr-python?tab=readme-ov-file#readme)
+[](https://github.com/cssnr/vultr-python/graphs/contributors)
+[](https://github.com/cssnr/vultr-python/discussions)
+[](https://github.com/cssnr/vultr-python/forks)
+[](https://github.com/cssnr/vultr-python/stargazers)
+[](https://cssnr.github.io/)
+[](https://discord.gg/wXy6m2X8wY)
+[](https://ko-fi.com/cssnr)
+
# Vultr Python
+
+
+
+- [Install](#Install)
+- [Usage](#Usage)
+- [Support](#Support)
+- [Contributing](#Contributing)
+
Python 3 wrapper for the Vultr API v2.
-* Vultr: [https://www.vultr.com](https://www.vultr.com/?ref=6905748)
-* Vultr API: [https://www.vultr.com/api](https://www.vultr.com/api/?ref=6905748)
-* Vultr Python Docs: [https://cssnr.github.io/vultr-python](https://cssnr.github.io/vultr-python)
+[](https://github.com/cssnr/vultr-python?tab=readme-ov-file#readme)
+[](https://pypi.org/project/vultr-python)
+[](https://cssnr.github.io/vultr-python)
+[](https://www.vultr.com/api/?ref=6905748)
+
+Vultr API Reference: [https://www.vultr.com/api](https://www.vultr.com/api/?ref=6905748)
-This is currently a WIP and not complete, but has some useful functions.
-Feel free to request additional functions and more on [Discord](https://discord.gg/wXy6m2X8wY).
+> [!TIP]
+> This project is not complete, but has many useful functions.
+> Please submit a [Feature Request](https://github.com/cssnr/vultr-python/discussions/categories/feature-requests)
+> or report any [Issues](https://github.com/cssnr/vultr-python/issues).
+
+For more details visit [www.vultr.com](https://www.vultr.com/?ref=6905748).
## Install
-From PyPi using pip:
+From PyPi:
+
```text
python -m pip install vultr-python
```
-From Source using setuptools:
+From Source:
+
```text
git clone https://github.com/cssnr/vultr-python.git
-cd vultr-python
-python setup.py install
+python -m pip install vultr-python
```
## Usage
@@ -32,31 +63,40 @@ python setup.py install
You will need to create an api key and whitelist your IP address.
Most functions do not work without an API Key.
-* [https://my.vultr.com/settings/#settingsapi](https://my.vultr.com/settings/#settingsapi)
+- [https://my.vultr.com/settings/#settingsapi](https://my.vultr.com/settings/#settingsapi)
Initialize the class with your API Key or with the `VULTR_API_KEY` environment variable.
+
```python
from vultr import Vultr
vultr = Vultr('VULTR_API_KEY')
```
+
List plans and get available regions for that plan
+
```python
plans = vultr.list_plans()
plan = plans[0] # 0 seems to be the basic 5 dollar plan
regions = vultr.list_regions()
available = vultr.filter_regions(regions, plan['locations'])
```
+
Get the OS list and filter by name
+
```python
os_list = vultr.list_os()
ubuntu_lts = vultr.filter_os(os_list, 'Ubuntu 20.04 x64')
```
+
Create a new ssh key from key string
+
```python
sshkey = vultr.create_key('key-name', 'ssh-rsa AAAA...')
```
+
Create a new instance
+
```python
hostname = 'my-new-host'
data = {
@@ -70,8 +110,31 @@ data = {
instance = vultr.create_instance(**data)
```
-View all functions at the Doc site: [https://cssnr.github.io/vultr-python](https://cssnr.github.io/vultr-python)
+Full Documentation: [https://cssnr.github.io/vultr-python](https://cssnr.github.io/vultr-python)
+
+Vultr API Reference: [https://www.vultr.com/api](https://www.vultr.com/api/?ref=6905748)
+
+# Support
+
+For general help or to request a feature, see:
+
+- Q&A Discussion:
+- Request a Feature:
+- Chat with us on Discord:
+
+If you are experiencing an issue/bug or getting unexpected results, you can:
+
+- Report an Issue:
+- Provide General Feedback: [https://cssnr.github.io/feedback/](https://cssnr.github.io/feedback/?app=vultr-python)
+- Chat with us on Discord:
+
+# Contributing
+
+If you would like to submit a PR, please review the [CONTRIBUTING.md](#contributing-ov-file).
+
+Please consider making a donation to support the development of this project
+and [additional](https://cssnr.com/) open source projects.
-View the full API documentation at Vultr: [https://www.vultr.com/api](https://www.vultr.com/api/?ref=6905748)
+[](https://ko-fi.com/cssnr)
-If you have more questions, concerns, or comments? Join our [Discord](https://discord.gg/wXy6m2X8wY) for more information...
+For a full list of current projects visit: [https://cssnr.github.io/](https://cssnr.github.io/)
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..c27277d
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,93 @@
+[](https://pypi.org/project/vultr-python/)
+[](https://pypistats.org/packages/vultr-python)
+[](https://clickpy.clickhouse.com/dashboard/vultr-python)
+[](https://app.codacy.com/gh/cssnr/vultr-python/dashboard)
+[](https://sonarcloud.io/summary/new_code?id=cssnr_vultr-python)
+[](https://github.com/cssnr/vultr-python/graphs/contributors)
+[](https://github.com/cssnr/vultr-python/discussions)
+[](https://github.com/cssnr/vultr-python/forks)
+[](https://github.com/cssnr/vultr-python/stargazers)
+[](https://cssnr.github.io/)
+[](https://discord.gg/wXy6m2X8wY)
+[](https://ko-fi.com/cssnr)
+
+Python 3 wrapper for the [Vultr](https://www.vultr.com/?ref=6905748) API v2.
+
+[](https://github.com/cssnr/vultr-python?tab=readme-ov-file#readme)
+[](https://pypi.org/project/vultr-python)
+[](#Vultr)
+[](https://www.vultr.com/api/?ref=6905748)
+
+[⬇️ Jump to the API Documentation](#Vultr)
+
+## Install
+
+From PyPi:
+
+```text
+python -m pip install vultr-python
+```
+
+From Source:
+
+```text
+git clone https://github.com/cssnr/vultr-python.git
+python -m pip install vultr-python
+```
+
+## Usage
+
+You will need to create an api key and whitelist your IP address. Most functions do not work without an API Key.
+
+- [https://my.vultr.com/settings/#settingsapi](https://my.vultr.com/settings/#settingsapi)
+
+Initialize the class with your API Key or with the `VULTR_API_KEY` environment variable.
+
+```python
+from vultr import Vultr
+
+vultr = Vultr('VULTR_API_KEY')
+```
+
+List plans and get available regions for that plan
+
+```python
+plans = vultr.list_plans()
+plan = plans[0] # 0 seems to be the basic 5 dollar plan
+regions = vultr.list_regions()
+available = vultr.filter_regions(regions, plan['locations'])
+```
+
+Get the OS list and filter by name
+
+```python
+os_list = vultr.list_os()
+ubuntu_lts = vultr.filter_os(os_list, 'Ubuntu 20.04 x64')
+```
+
+Create a new ssh key from key string
+
+```python
+sshkey = vultr.create_key('key-name', 'ssh-rsa AAAA...')
+```
+
+Create a new instance
+
+```python
+hostname = 'my-new-host'
+data = {
+ 'region': available[0]['id'],
+ 'plan': plan['id'],
+ 'os_id': ubuntu_lts['id'],
+ 'sshkey_id': [sshkey['id']],
+ 'hostname': hostname,
+ 'label': hostname,
+}
+instance = vultr.create_instance(**data)
+```
+
+
+
+Vultr API Reference: [https://www.vultr.com/api](https://www.vultr.com/api/?ref=6905748)
+
+---
diff --git a/docs/module.html.jinja2 b/docs/module.html.jinja2
new file mode 100644
index 0000000..229f2a8
--- /dev/null
+++ b/docs/module.html.jinja2
@@ -0,0 +1,2 @@
+{% extends "default/module.html.jinja2" %}
+{% block title %}Vultr Python{% endblock %}
diff --git a/docs/syntax-highlighting.css b/docs/syntax-highlighting.css
new file mode 100644
index 0000000..e5ec354
--- /dev/null
+++ b/docs/syntax-highlighting.css
@@ -0,0 +1,246 @@
+/* monokai color scheme, see pdoc/template/README.md */
+pre {
+ line-height: 125%;
+}
+span.linenos {
+ color: inherit;
+ background-color: transparent;
+ padding-left: 5px;
+ padding-right: 20px;
+}
+.pdoc-code .hll {
+ background-color: #49483e;
+}
+.pdoc-code {
+ background: #272822;
+ color: #f8f8f2;
+}
+.pdoc-code .c {
+ color: #75715e;
+} /* Comment */
+.pdoc-code .err {
+ color: #960050;
+ background-color: #1e0010;
+} /* Error */
+.pdoc-code .esc {
+ color: #f8f8f2;
+} /* Escape */
+.pdoc-code .g {
+ color: #f8f8f2;
+} /* Generic */
+.pdoc-code .k {
+ color: #66d9ef;
+} /* Keyword */
+.pdoc-code .l {
+ color: #ae81ff;
+} /* Literal */
+.pdoc-code .n {
+ color: #f8f8f2;
+} /* Name */
+.pdoc-code .o {
+ color: #f92672;
+} /* Operator */
+.pdoc-code .x {
+ color: #f8f8f2;
+} /* Other */
+.pdoc-code .p {
+ color: #f8f8f2;
+} /* Punctuation */
+.pdoc-code .ch {
+ color: #75715e;
+} /* Comment.Hashbang */
+.pdoc-code .cm {
+ color: #75715e;
+} /* Comment.Multiline */
+.pdoc-code .cp {
+ color: #75715e;
+} /* Comment.Preproc */
+.pdoc-code .cpf {
+ color: #75715e;
+} /* Comment.PreprocFile */
+.pdoc-code .c1 {
+ color: #75715e;
+} /* Comment.Single */
+.pdoc-code .cs {
+ color: #75715e;
+} /* Comment.Special */
+.pdoc-code .gd {
+ color: #f92672;
+} /* Generic.Deleted */
+.pdoc-code .ge {
+ color: #f8f8f2;
+ font-style: italic;
+} /* Generic.Emph */
+.pdoc-code .gr {
+ color: #f8f8f2;
+} /* Generic.Error */
+.pdoc-code .gh {
+ color: #f8f8f2;
+} /* Generic.Heading */
+.pdoc-code .gi {
+ color: #a6e22e;
+} /* Generic.Inserted */
+.pdoc-code .go {
+ color: #66d9ef;
+} /* Generic.Output */
+.pdoc-code .gp {
+ color: #f92672;
+ font-weight: bold;
+} /* Generic.Prompt */
+.pdoc-code .gs {
+ color: #f8f8f2;
+ font-weight: bold;
+} /* Generic.Strong */
+.pdoc-code .gu {
+ color: #75715e;
+} /* Generic.Subheading */
+.pdoc-code .gt {
+ color: #f8f8f2;
+} /* Generic.Traceback */
+.pdoc-code .kc {
+ color: #66d9ef;
+} /* Keyword.Constant */
+.pdoc-code .kd {
+ color: #66d9ef;
+} /* Keyword.Declaration */
+.pdoc-code .kn {
+ color: #f92672;
+} /* Keyword.Namespace */
+.pdoc-code .kp {
+ color: #66d9ef;
+} /* Keyword.Pseudo */
+.pdoc-code .kr {
+ color: #66d9ef;
+} /* Keyword.Reserved */
+.pdoc-code .kt {
+ color: #66d9ef;
+} /* Keyword.Type */
+.pdoc-code .ld {
+ color: #e6db74;
+} /* Literal.Date */
+.pdoc-code .m {
+ color: #ae81ff;
+} /* Literal.Number */
+.pdoc-code .s {
+ color: #e6db74;
+} /* Literal.String */
+.pdoc-code .na {
+ color: #a6e22e;
+} /* Name.Attribute */
+.pdoc-code .nb {
+ color: #f8f8f2;
+} /* Name.Builtin */
+.pdoc-code .nc {
+ color: #a6e22e;
+} /* Name.Class */
+.pdoc-code .no {
+ color: #66d9ef;
+} /* Name.Constant */
+.pdoc-code .nd {
+ color: #a6e22e;
+} /* Name.Decorator */
+.pdoc-code .ni {
+ color: #f8f8f2;
+} /* Name.Entity */
+.pdoc-code .ne {
+ color: #a6e22e;
+} /* Name.Exception */
+.pdoc-code .nf {
+ color: #a6e22e;
+} /* Name.Function */
+.pdoc-code .nl {
+ color: #f8f8f2;
+} /* Name.Label */
+.pdoc-code .nn {
+ color: #f8f8f2;
+} /* Name.Namespace */
+.pdoc-code .nx {
+ color: #a6e22e;
+} /* Name.Other */
+.pdoc-code .py {
+ color: #f8f8f2;
+} /* Name.Property */
+.pdoc-code .nt {
+ color: #f92672;
+} /* Name.Tag */
+.pdoc-code .nv {
+ color: #f8f8f2;
+} /* Name.Variable */
+.pdoc-code .ow {
+ color: #f92672;
+} /* Operator.Word */
+.pdoc-code .w {
+ color: #f8f8f2;
+} /* Text.Whitespace */
+.pdoc-code .mb {
+ color: #ae81ff;
+} /* Literal.Number.Bin */
+.pdoc-code .mf {
+ color: #ae81ff;
+} /* Literal.Number.Float */
+.pdoc-code .mh {
+ color: #ae81ff;
+} /* Literal.Number.Hex */
+.pdoc-code .mi {
+ color: #ae81ff;
+} /* Literal.Number.Integer */
+.pdoc-code .mo {
+ color: #ae81ff;
+} /* Literal.Number.Oct */
+.pdoc-code .sa {
+ color: #e6db74;
+} /* Literal.String.Affix */
+.pdoc-code .sb {
+ color: #e6db74;
+} /* Literal.String.Backtick */
+.pdoc-code .sc {
+ color: #e6db74;
+} /* Literal.String.Char */
+.pdoc-code .dl {
+ color: #e6db74;
+} /* Literal.String.Delimiter */
+.pdoc-code .sd {
+ color: #e6db74;
+} /* Literal.String.Doc */
+.pdoc-code .s2 {
+ color: #e6db74;
+} /* Literal.String.Double */
+.pdoc-code .se {
+ color: #ae81ff;
+} /* Literal.String.Escape */
+.pdoc-code .sh {
+ color: #e6db74;
+} /* Literal.String.Heredoc */
+.pdoc-code .si {
+ color: #e6db74;
+} /* Literal.String.Interpol */
+.pdoc-code .sx {
+ color: #e6db74;
+} /* Literal.String.Other */
+.pdoc-code .sr {
+ color: #e6db74;
+} /* Literal.String.Regex */
+.pdoc-code .s1 {
+ color: #e6db74;
+} /* Literal.String.Single */
+.pdoc-code .ss {
+ color: #e6db74;
+} /* Literal.String.Symbol */
+.pdoc-code .bp {
+ color: #f8f8f2;
+} /* Name.Builtin.Pseudo */
+.pdoc-code .fm {
+ color: #a6e22e;
+} /* Name.Function.Magic */
+.pdoc-code .vc {
+ color: #f8f8f2;
+} /* Name.Variable.Class */
+.pdoc-code .vg {
+ color: #f8f8f2;
+} /* Name.Variable.Global */
+.pdoc-code .vi {
+ color: #f8f8f2;
+} /* Name.Variable.Instance */
+.pdoc-code .vm {
+ color: #f8f8f2;
+} /* Name.Variable.Magic */
diff --git a/docs/theme.css b/docs/theme.css
new file mode 100644
index 0000000..60b447a
--- /dev/null
+++ b/docs/theme.css
@@ -0,0 +1,20 @@
+:root {
+ --pdoc-background: #212529;
+}
+
+.pdoc {
+ --text: #f7f7f7;
+ --muted: #9d9d9d;
+ --link: #58a6ff;
+ --link-hover: #3989ff;
+ --code: #333;
+ --active: #555;
+
+ --accent: #343434;
+ --accent2: #555;
+
+ --nav-hover: rgba(0, 0, 0, 0.1);
+ --name: #77c1ff;
+ --def: #0cdd0c;
+ --annotation: #00c037;
+}
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..052de90
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,104 @@
+# Project
+[project]
+name = "vultr-python"
+description = "Python 3 wrapper for the Vultr API v2.0"
+authors = [{ name="Shane" }]
+readme = "README.md"
+dynamic = ["version"]
+requires-python = ">=3.6"
+classifiers = [
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: 3.06",
+ "Programming Language :: Python :: 3.07",
+ "Programming Language :: Python :: 3.08",
+ "Programming Language :: Python :: 3.09",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+ "Operating System :: OS Independent",
+ "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
+ "Natural Language :: English",
+]
+license = { text = "MIT" }
+#license = { file = "LICENSE" }
+
+dependencies = ["requests"]
+
+[dependency-groups]
+dev = [
+ "bandit",
+ "black",
+ "build",
+ "coverage",
+ "isort",
+ "mypy",
+ "requests",
+ "ruff",
+ "setuptools",
+ "ty",
+ "types-requests",
+]
+
+[project.urls]
+Homepage = "https://cssnr.com/"
+Documentation = "https://cssnr.github.io/vultr-python"
+Source = "https://github.com/cssnr/vultr-python"
+Issues = "https://github.com/cssnr/vultr-python/issues"
+Funding = "https://ko-fi.com/cssnr"
+
+# Setup Tools
+#[tool.setuptools.package-data]
+#vultr = ["py.typed"]
+[tool.setuptools.dynamic]
+version = { attr = "vultr.version.__version__" }
+
+# Build System
+[build-system]
+requires = ["setuptools>=61.0"]
+build-backend = "setuptools.build_meta"
+
+# Bandit
+# https://bandit.readthedocs.io/en/latest/config.html
+[tool.bandit]
+exclude_dirs = ["venv"]
+skips = ["B105", "B107"]
+
+# Black
+# https://black.readthedocs.io/en/stable/usage_and_configuration/
+[tool.black]
+line-length = 119
+extend-exclude = '(\.github)'
+
+# Coverage
+# https://coverage.readthedocs.io/en/latest/
+[tool.coverage.run]
+omit = ["*.egg-info/*", ".github/*"]
+source = ["src"]
+
+# Isort
+# https://pycqa.github.io/isort/docs/configuration/options.html
+[tool.isort]
+profile = "black"
+lines_after_imports = 2
+src_paths = ["src", "test"]
+skip = ["venv", ".github"]
+
+# Mypy
+# https://mypy.readthedocs.io/en/stable/config_file.html
+[tool.mypy]
+#ignore_missing_imports = true
+exclude = ["venv"]
+
+# Ruff
+# https://docs.astral.sh/ruff/configuration/
+[tool.ruff]
+line-length = 119
+target-version = "py313"
+extend-exclude = [".github"]
+
+[tool.ruff.lint]
+select = ["E4", "E7", "E9", "F", "B", "Q"]
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index f229360..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-requests
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 8fbaec7..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-[flake8]
-exclude = venv,*migrations*
-max-line-length = 119
-
-[coverage:run]
-omit = *venv*,*migrations*
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 2d26b5b..0000000
--- a/setup.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import os
-from setuptools import setup
-
-
-with open(os.path.join(os.path.dirname(__file__), 'README.md'), 'r') as f:
- long_description = f.read()
-
-setup(
- name='vultr-python',
- version='0.1.5',
- description='Python 3 wrapper for the Vultr API v2.0',
- long_description=long_description,
- long_description_content_type="text/markdown",
- url='https://github.com/cssnr/vultr-python',
- author='Shane',
- author_email='shane@sapps.me',
- py_modules=['vultr'],
- install_requires=['requests'],
- python_requires='>=3.5',
- include_package_data=True,
- zip_safe=False,
- platforms='any',
- project_urls={
- 'Documentation': 'https://cssnr.github.io/vultr-python',
- 'Source': 'https://github.com/cssnr/vultr-python',
- },
- classifiers=[
- 'Development Status :: 3 - Alpha',
- 'Intended Audience :: Developers',
- 'Intended Audience :: System Administrators',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python :: 3 :: Only',
- ],
-)
diff --git a/src/vultr/__init__.py b/src/vultr/__init__.py
new file mode 100644
index 0000000..6cc4cfb
--- /dev/null
+++ b/src/vultr/__init__.py
@@ -0,0 +1,8 @@
+"""
+.. include:: ../../docs/index.md
+"""
+
+from .vultr import Vultr
+
+
+__all__ = ["Vultr"]
diff --git a/src/vultr/version.py b/src/vultr/version.py
new file mode 100644
index 0000000..bf9d384
--- /dev/null
+++ b/src/vultr/version.py
@@ -0,0 +1,12 @@
+import os
+import re
+
+
+def get_version() -> str:
+ version = os.environ.get("GITHUB_REF_NAME", "0.0.1")
+ pattern = r"^\d+\.\d+\.\d+(?:[abc]\d*)?$"
+ match = re.match(pattern, version)
+ return version if match else "0.0.1"
+
+
+__version__ = get_version()
diff --git a/vultr.py b/src/vultr/vultr.py
similarity index 59%
rename from vultr.py
rename to src/vultr/vultr.py
index 108a745..96f0b50 100644
--- a/vultr.py
+++ b/src/vultr/vultr.py
@@ -1,146 +1,147 @@
import os
-import requests
from typing import Optional, Union
+import requests
+
class Vultr(object):
- url = 'https://api.vultr.com/v2'
+ url = "https://api.vultr.com/v2"
def __init__(self, api_key: Optional[str] = None):
"""
:param str api_key: Vultr API Key or VULTR_API_KEY environment variable
"""
- self.api_key = api_key or os.getenv('VULTR_API_KEY')
+ self.api_key = api_key or os.getenv("VULTR_API_KEY")
self.s = requests.session()
if self.api_key:
- self.s.headers.update({'Authorization': f'Bearer {self.api_key}'})
+ self.s.headers.update({"Authorization": f"Bearer {self.api_key}"})
def list_os(self):
- url = f'{self.url}/os'
- return self._get(url)['os']
+ url = f"{self.url}/os"
+ return self._get(url)["os"]
def list_plans(self):
- url = f'{self.url}/plans'
- return self._get(url)['plans']
+ url = f"{self.url}/plans"
+ return self._get(url)["plans"]
def list_regions(self):
- url = f'{self.url}/regions'
- return self._get(url)['regions']
+ url = f"{self.url}/regions"
+ return self._get(url)["regions"]
def list_instances(self):
- url = f'{self.url}/instances'
- return self._get(url)['instances']
+ url = f"{self.url}/instances"
+ return self._get(url)["instances"]
def get_instance(self, instance: Union[str, dict]):
instance_id = self._get_obj_key(instance)
- url = f'{self.url}/instances/{instance_id}'
- return self._get(url)['instance']
+ url = f"{self.url}/instances/{instance_id}"
+ return self._get(url)["instance"]
def create_instance(self, region: str, plan: str, **kwargs):
- data = {'region': region, 'plan': plan}
+ data = {"region": region, "plan": plan}
data.update(kwargs)
- url = f'{self.url}/instances'
- return self._post(url, data)['instance']
+ url = f"{self.url}/instances"
+ return self._post(url, data)["instance"]
def update_instance(self, instance: Union[str, dict], **kwargs):
instance_id = self._get_obj_key(instance)
- url = f'{self.url}/instances/{instance_id}'
- return self._patch(url, kwargs)['instance']
+ url = f"{self.url}/instances/{instance_id}"
+ return self._patch(url, kwargs)["instance"]
def delete_instance(self, instance: Union[str, dict]):
instance_id = self._get_obj_key(instance)
- url = f'{self.url}/instances/{instance_id}'
+ url = f"{self.url}/instances/{instance_id}"
return self._delete(url)
def list_keys(self):
- url = f'{self.url}/ssh-keys'
- return self._get(url)['ssh_keys']
+ url = f"{self.url}/ssh-keys"
+ return self._get(url)["ssh_keys"]
def get_key(self, key: Union[str, dict]):
key_id = self._get_obj_key(key)
- url = f'{self.url}/ssh-keys/{key_id}'
- return self._get(url)['ssh_key']
+ url = f"{self.url}/ssh-keys/{key_id}"
+ return self._get(url)["ssh_key"]
def create_key(self, name: str, key: str, **kwargs):
- data = {'name': name, 'ssh_key': key}
+ data = {"name": name, "ssh_key": key}
data.update(kwargs)
- url = f'{self.url}/ssh-keys'
- return self._post(url, data)['ssh_key']
+ url = f"{self.url}/ssh-keys"
+ return self._post(url, data)["ssh_key"]
def update_key(self, key: Union[str, dict], **kwargs):
key_id = self._get_obj_key(key)
- url = f'{self.url}/ssh-keys/{key_id}'
- return self._patch(url, kwargs)['ssh_key']
+ url = f"{self.url}/ssh-keys/{key_id}"
+ return self._patch(url, kwargs)["ssh_key"]
def delete_key(self, key: Union[str, dict]):
key_id = self._get_obj_key(key)
- url = f'{self.url}/ssh-keys/{key_id}'
+ url = f"{self.url}/ssh-keys/{key_id}"
return self._delete(url)
def list_scripts(self):
- url = f'{self.url}/startup-scripts'
- return self._get(url)['startup_scripts']
+ url = f"{self.url}/startup-scripts"
+ return self._get(url)["startup_scripts"]
def get_script(self, script: Union[str, dict]):
script_id = self._get_obj_key(script)
- url = f'{self.url}/startup-scripts/{script_id}'
- return self._get(url)['startup_script']
+ url = f"{self.url}/startup-scripts/{script_id}"
+ return self._get(url)["startup_script"]
def create_script(self, name: str, script: str, **kwargs):
- data = {'name': name, 'script': script}
+ data = {"name": name, "script": script}
data.update(kwargs)
- url = f'{self.url}/startup-scripts'
- return self._post(url, data)['startup_script']
+ url = f"{self.url}/startup-scripts"
+ return self._post(url, data)["startup_script"]
def update_script(self, script: Union[str, dict], **kwargs):
script_id = self._get_obj_key(script)
- url = f'{self.url}/startup-scripts/{script_id}'
- return self._patch(url, kwargs)['startup_script']
+ url = f"{self.url}/startup-scripts/{script_id}"
+ return self._patch(url, kwargs)["startup_script"]
def delete_script(self, script: Union[str, dict]):
script_id = self._get_obj_key(script)
- url = f'{self.url}/startup-scripts/{script_id}'
+ url = f"{self.url}/startup-scripts/{script_id}"
return self._delete(url)
def list_ipv4(self, instance: Union[str, dict]):
instance_id = self._get_obj_key(instance)
- url = f'{self.url}/instances/{instance_id}/ipv4'
- return self._get(url)['ipv4s']
+ url = f"{self.url}/instances/{instance_id}/ipv4"
+ return self._get(url)["ipv4s"]
def create_ipv4(self, instance: Union[str, dict], **kwargs):
instance_id = self._get_obj_key(instance)
- url = f'{self.url}/instances/{instance_id}/ipv4'
- return self._post(url, kwargs)['ipv4']
+ url = f"{self.url}/instances/{instance_id}/ipv4"
+ return self._post(url, kwargs)["ipv4"]
def delete_ipv4(self, instance: Union[str, dict]):
instance_id = self._get_obj_key(instance)
- url = f'{self.url}/instances/{instance_id}/ipv4'
+ url = f"{self.url}/instances/{instance_id}/ipv4"
return self._delete(url)
@staticmethod
def filter_keys(keys: list, name: str) -> dict:
try:
- return next(d for d in keys if d['name'].lower() == name.lower())
+ return next(d for d in keys if d["name"].lower() == name.lower())
except StopIteration:
return {}
@staticmethod
def filter_os(os_list: list, name: str) -> dict:
try:
- return next(d for d in os_list if d['name'].lower() == name.lower())
+ return next(d for d in os_list if d["name"].lower() == name.lower())
except StopIteration:
return {}
@staticmethod
def filter_scripts(scripts: list, name: str) -> dict:
try:
- return next(d for d in scripts if d['name'].lower() == name.lower())
+ return next(d for d in scripts if d["name"].lower() == name.lower())
except StopIteration:
return {}
@staticmethod
def filter_regions(regions: list, locations: list) -> list:
- return [d for d in regions if d['id'] in locations]
+ return [d for d in regions if d["id"] in locations]
def _get(self, url):
r = self.s.get(url, timeout=10)
@@ -167,7 +168,7 @@ def _delete(self, url):
return None
@staticmethod
- def _get_obj_key(obj, key='id'):
+ def _get_obj_key(obj, key="id"):
if isinstance(obj, str):
return obj
elif isinstance(obj, int):
@@ -176,4 +177,4 @@ def _get_obj_key(obj, key='id'):
if key in obj:
return obj[key]
else:
- raise ValueError(f'Unable to parse object: {key}')
+ raise ValueError(f"Unable to parse object: {key}")