diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000..c521d36 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,27 @@ +name: Changelog + +on: + pull_request: + types: [labeled, unlabeled, opened, reopened, synchronize] + +permissions: + contents: read + +env: + PR_NUMBER: ${{ github.event.number }} + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Check for file + if: ${{ + hashFiles(format('docs/changes/{0}.*.rst', github.event.number)) == '' && + ! contains( github.event.pull_request.labels.*.name, 'skip-changelog') + }} + run: | + ls docs/changes + echo ERROR: No changelog file found for pull request \#"$PR_NUMBER"! + exit 1 + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7844163..8692666 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,3 @@ -# This workflow will install Python dependencies, run tests and lint with a single version of Python -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python - name: CI on: @@ -21,7 +18,7 @@ jobs: python-version: ["3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -31,7 +28,7 @@ jobs: - name: Install dependencies run: | pip install --upgrade pip - pip install -e ".[tests]" + pip install -e . --group tests pip freeze - name: Test with pytest @@ -43,18 +40,24 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} - # pre-commit: - # runs-on: ubuntu-latest - # strategy: - # matrix: - # python-version: ["3.14"] - # - # steps: - # - uses: actions/checkout@v4 - # - name: Set up Python ${{ matrix.python-version }} - # uses: actions/setup-python@v5 - # with: - # python-version: ${{ matrix.python-version }} - # - name: Display Python version - # run: python -c "import sys; print(sys.version)" - # - uses: pre-commit/action@v3.0.1 + test_docs: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.14' + architecture: 'x64' + - name: Display Python version + run: python -c "import sys; print(sys.version)" + - name: Install documentation dependencies + run: | + pip install --upgrade pip + pip install -e . --group docs + pip freeze + - name: Build documentation + run: | + make -C docs html diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ff9fdc4 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,90 @@ +name: Docs + +on: + release: + types: [published] + +permissions: + contents: write + +jobs: + build_and_release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.14' + architecture: 'x64' + - name: Display Python version + run: python -c "import sys; print(sys.version)" + - name: Install dependencies + run: | + pip install --upgrade pip + pip install -e . --group docs + pip freeze + - name: Build towncrier + run: | + towncrier build --yes + + - name: Build sphinx docs + run: | + make -C docs html + - name: Compress build + run: | + sudo apt update --yes && sudo apt install zip --yes + cd docs/_build/html && zip -r ../html.zip ./* + cd ../../../ + + # The App Token creation and git config setup were adapted from: + # - create-github-app-token (https://github.com/actions/create-github-app-token) + # Originally licensed under MIT License. Copyright (c) 2023 Gregor Martynus, Parker Brown. + - name: Create GitHub App token + id: app-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Get GitHub App User ID + id: get-user-id + run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + - name: Setup git config + run: | + set +x + git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]' + git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com' + - name: Commit and Push + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + set +x + git add -u docs/changes/ + git add docs/changes/*.rst + git commit -m "Create Changelog for ${{ github.event.release.tag_name }}" || exit 0 + + git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:${{ github.event.release.target_commitish }} + + # Inspired by: https://stackoverflow.com/a/60479844 and https://stackoverflow.com/a/70447517 + - name: Create SSH key + run: | + install -m 600 -D /dev/null ~/.ssh/id_key + echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_key + ssh-keyscan -H "$SSH_HOSTNAME" > ~/.ssh/known_hosts + env: + SSH_PRIVATE_KEY: ${{secrets.SSH_PRIVATE_KEY}} + SSH_HOSTNAME: ${{secrets.SSH_HOSTNAME}} + - name: Upload to server + run: | + ssh -v -i ~/.ssh/id_key "$SSH_USER@$SSH_HOSTNAME" "rm -rf '$SSH_TARGET_DIR/' && mkdir -p '$SSH_TARGET_DIR'" + scp -v -i ~/.ssh/id_key -r docs/_build/html.zip "$SSH_USER"@"$SSH_HOSTNAME":"$SSH_TARGET_DIR"/html.zip + ssh -v -i ~/.ssh/id_key "$SSH_USER@$SSH_HOSTNAME" "unzip -o '$SSH_TARGET_DIR/html.zip' -d '$SSH_TARGET_DIR' && rm -rf '$SSH_TARGET_DIR/html.zip'" + env: + SSH_USER: ${{secrets.SSH_USER}} + SSH_HOSTNAME: ${{secrets.SSH_HOSTNAME}} + SSH_TARGET_DIR: ${{secrets.SSH_TARGET_DIR}} diff --git a/.gitignore b/.gitignore index 633597b..6c51ea9 100644 --- a/.gitignore +++ b/.gitignore @@ -80,7 +80,7 @@ instance/ # Sphinx documentation docs/_build/ -docs/build +docs/_generated # PyBuilder .pybuilder/ diff --git a/ASSETS_LEGAL.rst b/ASSETS_LEGAL.rst index 5d14269..f1d08cf 100644 --- a/ASSETS_LEGAL.rst +++ b/ASSETS_LEGAL.rst @@ -1,17 +1,16 @@ -============== Asset Licenses -============== +-------------- This file documents the sources and licenses of additional media used within this repository, which is not inside of the main source code of the module. **Backpy Logo and Icon** - =================== =================================================================================================================================================================== - **File** ``_ ``_ + =================== ========================================================================================================================================================================================================================================================================================== + **File** ``_ ``_ ``_ ``_ ``_ ``_ **Author** ``_ **Used Elements** | `Font Awesome 'database' icon `_ (License: `CC-4.0-BY `_) | `Catppuccin Colors `_ (License: `MIT License `_) - =================== =================================================================================================================================================================== + =================== ========================================================================================================================================================================================================================================================================================== diff --git a/LICENSE b/LICENSE index b7a4139..92bfde8 100644 --- a/LICENSE +++ b/LICENSE @@ -675,7 +675,7 @@ Public License instead of this License. But first, please read ### Third-party code notice -This project includes adapted code from the following sources: +This project includes adapted code or assets from the following sources: - pyvisgen (https://github.com/radionets-project/pyvisgen) Licensed under the MIT License. @@ -685,7 +685,15 @@ This project includes adapted code from the following sources: Licensed under the BSD 3-Clause License. Copyright (c) 2011-2024, Astropy Developers -The original MIT License and BSD 3-Clause License texts are included below: +- Fira Sans Font (https://fonts.google.com/specimen/Fira+Sans) + Licensed under the SIL Open Font License, Version 1.1. + Copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. + +- JetBrains Mono Font (https://fonts.google.com/specimen/JetBrains+Mono) + Licensed under the SIL Open Font License, Version 1.1. + Copyright (c) 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +The original MIT License, BSD 3-Clause License and SIL Open Font License, Version 1.1 texts are included below: -------------------- @@ -738,4 +746,93 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PRO OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------- + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 0347b88..b5a86dd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # backpy [![CI Status](https://github.com/tgross03/backpy/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/tgross03/backpy/actions/workflows/ci.yml?branch=main) [![codecov](https://codecov.io/gh/tgross03/backpy/graph/badge.svg?token=NSQD951ZPJ)](https://codecov.io/gh/tgross03/backpy) [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/tgross03/backpy/main.svg)](https://results.pre-commit.ci/latest/github/tgross03/backpy/main) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) -[![BackPy Logo](./assets/backpy_logo.png)](https://github.com/tgross03/backpy) +[![BackPy Logo](./docs/_static/logos/backpy_logo_dark.png)](https://github.com/tgross03/backpy) > [!CAUTION] > This package is still in development and not stable at this time! Features and functionalities might not work as expected. diff --git a/assets/backpy_icon.png b/assets/backpy_icon.png deleted file mode 100644 index b0a0d7f..0000000 Binary files a/assets/backpy_icon.png and /dev/null differ diff --git a/assets/backpy_logo.png b/assets/backpy_logo.png deleted file mode 100644 index 1d3975f..0000000 Binary files a/assets/backpy_logo.png and /dev/null differ diff --git a/backpy/__init__.py b/backpy/__init__.py index e8662c8..6a3cf5f 100644 --- a/backpy/__init__.py +++ b/backpy/__init__.py @@ -1,29 +1,19 @@ from __future__ import annotations -from backpy.core.config.configuration import TOMLConfiguration +from backpy.core.config import TOMLConfiguration from backpy.core.config.variables import VariableLibrary -VariableLibrary() +from backpy import version + +from rich import traceback -from backpy.core.backup import compression -from backpy.core.utils.times import TimeObject -from backpy.core.remote import Remote, Protocol +traceback.install(show_locals=True) -# Import in the correct order to avoid circular imports -from backpy.core.backup.types import BackupSpaceType -from backpy.core.backup.backup_space import BackupSpace -from backpy.core.backup.backup import Backup -from backpy.core.backup.file_backup_space import FileBackupSpace +VariableLibrary() __all__ = [ - "FileBackupSpace", - "TOMLConfiguration", "VariableLibrary", - "BackupSpace", - "BackupSpaceType", - "Backup", - "compression", - "TimeObject", - "Remote", - "Protocol", + "TOMLConfiguration", ] + +__version__ = version.__version__ diff --git a/backpy/cli/backup/create_command.py b/backpy/cli/backup/create_command.py index 56a889d..0f172ba 100644 --- a/backpy/cli/backup/create_command.py +++ b/backpy/cli/backup/create_command.py @@ -1,6 +1,5 @@ import rich_click as click -from backpy import BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupSpaceInput, @@ -9,6 +8,7 @@ TextInput, print_error_message, ) +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import ( BackupLimitExceededError, InvalidBackupSpaceError, diff --git a/backpy/cli/backup/delete_command.py b/backpy/cli/backup/delete_command.py index bb7a82c..f1fd3e4 100644 --- a/backpy/cli/backup/delete_command.py +++ b/backpy/cli/backup/delete_command.py @@ -1,7 +1,6 @@ import rich_click as click from rich.console import Console -from backpy import Backup, BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupInput, @@ -9,6 +8,8 @@ ConfirmInput, print_error_message, ) +from backpy.core.backup import Backup +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupError, InvalidBackupSpaceError palette = get_default_palette() diff --git a/backpy/cli/backup/info_command.py b/backpy/cli/backup/info_command.py index c083f17..ae91a0c 100644 --- a/backpy/cli/backup/info_command.py +++ b/backpy/cli/backup/info_command.py @@ -1,7 +1,6 @@ import rich_click as click from rich.console import Console -from backpy import Backup, BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupInput, @@ -9,6 +8,8 @@ ConfirmInput, print_error_message, ) +from backpy.core.backup import Backup +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupError, InvalidBackupSpaceError palette = get_default_palette() diff --git a/backpy/cli/backup/list_command.py b/backpy/cli/backup/list_command.py index bcc5c8e..cb8a638 100644 --- a/backpy/cli/backup/list_command.py +++ b/backpy/cli/backup/list_command.py @@ -2,9 +2,9 @@ from rich.console import Console from rich.tree import Tree -from backpy import BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import print_error_message +from backpy.core.space import BackupSpace from backpy.core.utils import bytes2str from backpy.core.utils.exceptions import InvalidBackupSpaceError diff --git a/backpy/cli/backup/lock_command.py b/backpy/cli/backup/lock_command.py index 22c93b5..146ccdf 100644 --- a/backpy/cli/backup/lock_command.py +++ b/backpy/cli/backup/lock_command.py @@ -1,7 +1,6 @@ import rich_click as click from rich.console import Console -from backpy import Backup, BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupInput, @@ -9,6 +8,8 @@ ConfirmInput, print_error_message, ) +from backpy.core.backup import Backup +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupError, InvalidBackupSpaceError palette = get_default_palette() diff --git a/backpy/cli/backup/restore_command.py b/backpy/cli/backup/restore_command.py index 3977d9b..3b4dc84 100644 --- a/backpy/cli/backup/restore_command.py +++ b/backpy/cli/backup/restore_command.py @@ -1,7 +1,6 @@ import rich_click as click from rich.console import Console -from backpy import Backup, BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupInput, @@ -10,6 +9,8 @@ TextInput, print_error_message, ) +from backpy.core.backup import Backup +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import ( InvalidBackupError, InvalidBackupSpaceError, diff --git a/backpy/cli/backup/unlock_command.py b/backpy/cli/backup/unlock_command.py index 00d9a90..511523c 100644 --- a/backpy/cli/backup/unlock_command.py +++ b/backpy/cli/backup/unlock_command.py @@ -1,7 +1,6 @@ import rich_click as click from rich.console import Console -from backpy import Backup, BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupInput, @@ -9,6 +8,8 @@ ConfirmInput, print_error_message, ) +from backpy.core.backup import Backup +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupError, InvalidBackupSpaceError palette = get_default_palette() diff --git a/backpy/cli/colors.py b/backpy/cli/colors.py index 903d15b..35cb62d 100644 --- a/backpy/cli/colors.py +++ b/backpy/cli/colors.py @@ -4,6 +4,8 @@ from backpy import VariableLibrary +__all__ = ["get_default_palette", "rgb_to_ansi"] + # RGB to ANSI guide from # https://jakob-bagterp.github.io/colorist-for-python/ansi-escape-codes/rgb-colors/ diff --git a/backpy/cli/config/set_command.py b/backpy/cli/config/set_command.py index e46ac24..abc4886 100644 --- a/backpy/cli/config/set_command.py +++ b/backpy/cli/config/set_command.py @@ -33,6 +33,26 @@ def set_value(key: str, value: str, force: bool, debug: bool) -> None: try: prev_value = VariableLibrary.get_variable(key=key) + if isinstance(prev_value, bool): + if value.lower() == "true": + value = True + elif value.lower() == "false": + value = False + else: + raise TypeError( + "The given value has to be boolean type (e.g. 'true' or 'false')!" + ) + elif isinstance(prev_value, int): + try: + value = int(value) + except ValueError: + raise TypeError("The given value has to be integer type (e.g. '1').") + elif isinstance(prev_value, float): + try: + value = float(value) + except ValueError: + raise TypeError("The given value has to be float type (e.g. '1.0').") + if prev_value == value: print( f"{palette.red}ERROR: {palette.maroon}The variable is already set " diff --git a/backpy/cli/elements.py b/backpy/cli/elements.py index c7cadc6..ca8554f 100644 --- a/backpy/cli/elements.py +++ b/backpy/cli/elements.py @@ -4,16 +4,55 @@ from fuzzyfinder import fuzzyfinder -from backpy import Backup, BackupSpace from backpy.cli.colors import EFFECTS, RESET, get_default_palette -from backpy.core.backup import Schedule +from backpy.core.backup import Backup, Schedule from backpy.core.remote import Remote +from backpy.core.space import BackupSpace from backpy.core.utils.utils import str2bytes palette = get_default_palette() +__all__ = [ + "print_error_message", + "ConfirmInput", + "TextInput", + "PasswordInput", + "BackupSpaceInput", + "BackupInput", + "RemoteInput", + "ScheduleInput", + "EnumerationInput", + "FilePathInput", + "DirectoryPathInput", + "IntegerInput", + "FloatInput", + "MemorySizeInput", + "_validate_always", + "_validate_not_none", + "_validate_file_path", + "_validate_directory_path", + "_validate_memory", + "_validate_integer", + "_validate_float", +] + def print_error_message(error: Exception, debug: bool) -> None: + """ + Print error messages depending on the debug mode. + If the debug mode is enabled, the exception is raised, + resulting in a stack trace. Otherwise, an error message is printed. + + Parameters + ---------- + + error: Exception + The exception to raise / print. + + debug: bool + Whether debug mode is enabled. + + """ if debug: raise error else: diff --git a/backpy/cli/remote/create_command.py b/backpy/cli/remote/create_command.py index 5b6ad58..62bdd68 100644 --- a/backpy/cli/remote/create_command.py +++ b/backpy/cli/remote/create_command.py @@ -2,7 +2,7 @@ import rich_click as click -from backpy import Protocol, Remote, VariableLibrary +from backpy import VariableLibrary from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( ConfirmInput, @@ -12,6 +12,7 @@ TextInput, print_error_message, ) +from backpy.core.remote import Protocol, Remote from backpy.core.remote.remote import protocols from backpy.core.utils.exceptions import InvalidRemoteError @@ -286,10 +287,11 @@ def create( try: remote.test_connection(verbosity_level=verbose) except Exception as e: - print_error_message(error=e, debug=debug) + remote.delete(delete_files=False, verbosity_level=verbose) print( - f"{palette.red}HINT:{palette.maroon} If you are experiencing connection problems " - "due to wrong settings of the remote, edit or remove it via the CLI." + f"{palette.red}ERROR:{palette.maroon} An error occurred while testing the connection! " + f"The remote has been deleted." ) + return print_error_message(error=e, debug=debug) return None diff --git a/backpy/cli/remote/edit_command.py b/backpy/cli/remote/edit_command.py index 582526a..6b8f2cb 100644 --- a/backpy/cli/remote/edit_command.py +++ b/backpy/cli/remote/edit_command.py @@ -2,9 +2,9 @@ import rich_click as click -from backpy import Protocol, Remote from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import PasswordInput, print_error_message +from backpy.core.remote import Protocol, Remote from backpy.core.remote.password import encrypt from backpy.core.remote.remote import protocols from backpy.core.utils.exceptions import InvalidRemoteError diff --git a/backpy/cli/schedule/activate_command.py b/backpy/cli/schedule/activate_command.py index b57a0ea..e0db45a 100644 --- a/backpy/cli/schedule/activate_command.py +++ b/backpy/cli/schedule/activate_command.py @@ -1,7 +1,6 @@ import rich_click as click from rich.console import Console -from backpy import BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupSpaceInput, @@ -10,6 +9,7 @@ print_error_message, ) from backpy.core.backup.scheduling import Schedule +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupSpaceError, InvalidScheduleError palette = get_default_palette() diff --git a/backpy/cli/schedule/create_command.py b/backpy/cli/schedule/create_command.py index c1390af..6cceae0 100644 --- a/backpy/cli/schedule/create_command.py +++ b/backpy/cli/schedule/create_command.py @@ -10,7 +10,8 @@ TextInput, print_error_message, ) -from backpy.core.backup import BackupSpace, Schedule +from backpy.core.backup import Schedule +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupSpaceError palette = get_default_palette() diff --git a/backpy/cli/schedule/deactivate_command.py b/backpy/cli/schedule/deactivate_command.py index d0d9afe..ac52ed4 100644 --- a/backpy/cli/schedule/deactivate_command.py +++ b/backpy/cli/schedule/deactivate_command.py @@ -1,7 +1,6 @@ import rich_click as click from rich.console import Console -from backpy import BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupSpaceInput, @@ -10,6 +9,7 @@ print_error_message, ) from backpy.core.backup.scheduling import Schedule +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupSpaceError, InvalidScheduleError palette = get_default_palette() diff --git a/backpy/cli/schedule/delete_command.py b/backpy/cli/schedule/delete_command.py index 0587eb4..97fa49f 100644 --- a/backpy/cli/schedule/delete_command.py +++ b/backpy/cli/schedule/delete_command.py @@ -1,7 +1,6 @@ import rich_click as click from rich.console import Console -from backpy import BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( BackupSpaceInput, @@ -10,6 +9,7 @@ print_error_message, ) from backpy.core.backup.scheduling import Schedule +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupSpaceError, InvalidScheduleError palette = get_default_palette() diff --git a/backpy/cli/schedule/list_command.py b/backpy/cli/schedule/list_command.py index aa71db7..b38361e 100644 --- a/backpy/cli/schedule/list_command.py +++ b/backpy/cli/schedule/list_command.py @@ -2,10 +2,10 @@ from rich.console import Console from rich.tree import Tree -from backpy import BackupSpace from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import print_error_message from backpy.core.backup import Schedule +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupSpaceError, InvalidScheduleError palette = get_default_palette() diff --git a/backpy/cli/space/clear_command.py b/backpy/cli/space/clear_command.py index 45e90cc..d5277ea 100644 --- a/backpy/cli/space/clear_command.py +++ b/backpy/cli/space/clear_command.py @@ -2,7 +2,7 @@ from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ConfirmInput, print_error_message -from backpy.core.backup import BackupSpace +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupSpaceError palette = get_default_palette() diff --git a/backpy/cli/space/common/create.py b/backpy/cli/space/common/create.py index ce88f92..6fac412 100644 --- a/backpy/cli/space/common/create.py +++ b/backpy/cli/space/common/create.py @@ -3,7 +3,7 @@ import rich_click as click from click_params import FirstOf -from backpy import BackupSpace, BackupSpaceType, Remote, VariableLibrary +from backpy import VariableLibrary from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ( ConfirmInput, @@ -15,6 +15,8 @@ print_error_message, ) from backpy.core.backup import compression +from backpy.core.remote import Remote +from backpy.core.space import BackupSpace, BackupSpaceType from backpy.core.utils.exceptions import InvalidRemoteError from backpy.core.utils.utils import str2bytes diff --git a/backpy/cli/space/common/edit.py b/backpy/cli/space/common/edit.py index c1267d3..eb29002 100644 --- a/backpy/cli/space/common/edit.py +++ b/backpy/cli/space/common/edit.py @@ -3,11 +3,12 @@ import rich_click as click from click_params import FirstOf -from backpy import BackupSpace, BackupSpaceType, Remote from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ConfirmInput, print_error_message from backpy.core.backup import compression from backpy.core.backup.compression import CompressionAlgorithm +from backpy.core.remote import Remote +from backpy.core.space import BackupSpace, BackupSpaceType from backpy.core.utils import bytes2str from backpy.core.utils.exceptions import InvalidBackupSpaceError, InvalidRemoteError from backpy.core.utils.utils import str2bytes @@ -90,7 +91,7 @@ def edit_backup_space( if isinstance(max_size, str): max_size = str2bytes(max_size) - if current_size > max_size: + if max_size != -1 and current_size > max_size: return print_error_message( error=ValueError( f"The given maximum disk usage of the backup space ({bytes2str(max_size)}) " diff --git a/backpy/cli/space/delete_command.py b/backpy/cli/space/delete_command.py index 1657f56..ae8ff8a 100644 --- a/backpy/cli/space/delete_command.py +++ b/backpy/cli/space/delete_command.py @@ -2,7 +2,7 @@ from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ConfirmInput, print_error_message -from backpy.core.backup import BackupSpace +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupSpaceError palette = get_default_palette() diff --git a/backpy/cli/space/file_system/create_command.py b/backpy/cli/space/file_system/create_command.py index 689f8cf..2f684e8 100644 --- a/backpy/cli/space/file_system/create_command.py +++ b/backpy/cli/space/file_system/create_command.py @@ -2,10 +2,10 @@ import rich_click as click -from backpy import BackupSpaceType from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import DirectoryPathInput from backpy.cli.space.common.create import common_options, create_backup_space +from backpy.core.space import BackupSpaceType palette = get_default_palette() diff --git a/backpy/cli/space/file_system/edit_command.py b/backpy/cli/space/file_system/edit_command.py index f37b903..0655041 100644 --- a/backpy/cli/space/file_system/edit_command.py +++ b/backpy/cli/space/file_system/edit_command.py @@ -2,10 +2,10 @@ import rich_click as click -from backpy import BackupSpaceType from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import ConfirmInput from backpy.cli.space.common.edit import common_options, edit_backup_space +from backpy.core.space import BackupSpaceType palette = get_default_palette() diff --git a/backpy/cli/space/info_command.py b/backpy/cli/space/info_command.py index 8767d84..7bbe4d6 100644 --- a/backpy/cli/space/info_command.py +++ b/backpy/cli/space/info_command.py @@ -3,7 +3,7 @@ from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import print_error_message -from backpy.core.backup import BackupSpace +from backpy.core.space import BackupSpace from backpy.core.utils.exceptions import InvalidBackupSpaceError palette = get_default_palette() diff --git a/backpy/cli/space/list_command.py b/backpy/cli/space/list_command.py index c812af3..93e690a 100644 --- a/backpy/cli/space/list_command.py +++ b/backpy/cli/space/list_command.py @@ -4,7 +4,7 @@ from backpy.cli.colors import RESET, get_default_palette from backpy.cli.elements import print_error_message -from backpy.core.backup import BackupSpace +from backpy.core.space import BackupSpace from backpy.core.utils import bytes2str palette = get_default_palette() diff --git a/backpy/core/backup/__init__.py b/backpy/core/backup/__init__.py index 8186638..c19f3f6 100644 --- a/backpy/core/backup/__init__.py +++ b/backpy/core/backup/__init__.py @@ -2,17 +2,10 @@ from backpy.core.backup import compression from backpy.core.backup.backup import Backup -from backpy.core.backup.backup_space import BackupSpace -from backpy.core.backup.file_backup_space import FileBackupSpace from backpy.core.backup.scheduling import Schedule -from backpy.core.backup.types import BackupSpaceType __all__ = [ "Backup", - "BackupSpace", - "BackupSpaceType", - "FileBackupSpace", - "compression", "Schedule", - "BackupSpaceType", + "compression", ] diff --git a/backpy/core/backup/backup.py b/backpy/core/backup/backup.py index ad95ffc..f2618f7 100644 --- a/backpy/core/backup/backup.py +++ b/backpy/core/backup/backup.py @@ -20,10 +20,12 @@ from backpy.core.utils.exceptions import InvalidBackupError, InvalidChecksumError if TYPE_CHECKING: - from backpy import BackupSpace + from backpy.core.space import BackupSpace palette = get_default_palette() +__all__ = ["Backup"] + class Backup: def __init__( @@ -161,7 +163,7 @@ def update_config(self, verbosity_level: int = 1): self._config.dump_dict(dict(merge({}, current_content, content))) if self.has_remote_archive(): - with self._remote(): + with self._remote(context_verbosity=verbosity_level): self._remote.remove( target=self.get_remote_config_path(), verbosity_level=verbosity_level, @@ -418,7 +420,6 @@ def new( cls._config.create() cls.update_config() - cls._config.prepend_no_edit_warning() if cls._backup_space.is_backup_limit_reached(post_creation=True): if cls._backup_space.is_auto_deletion_active(): diff --git a/backpy/core/backup/compression.py b/backpy/core/backup/compression.py index 970ebba..b2c5ac9 100644 --- a/backpy/core/backup/compression.py +++ b/backpy/core/backup/compression.py @@ -10,6 +10,15 @@ from backpy.core.utils.exceptions import UnsupportedCompressionAlgorithmError +__all__ = [ + "compress", + "unpack", + "is_algorithm_available", + "filter_paths", + "CompressionAlgorithm", + "_compression_methods", +] + @dataclass class CompressionAlgorithm: diff --git a/backpy/core/backup/scheduling.py b/backpy/core/backup/scheduling.py index 67a8328..d75c34f 100644 --- a/backpy/core/backup/scheduling.py +++ b/backpy/core/backup/scheduling.py @@ -17,10 +17,12 @@ COMMENT_SUFFIX = "(MANAGED BY BACKPY)" if TYPE_CHECKING: - from backpy.core.backup.backup_space import BackupSpace + from backpy.core.space.backup_space import BackupSpace palette = get_default_palette() +__all__ = ["Schedule"] + class Schedule: def __init__( @@ -137,7 +139,7 @@ def _get_comment(self) -> str: @classmethod def load_by_uuid(cls, unique_id: str) -> "Schedule": - from backpy.core.backup.backup_space import BackupSpace + from backpy.core.space.backup_space import BackupSpace config = TOMLConfiguration( path=Path(VariableLibrary.get_variable("paths.schedule_directory")) diff --git a/backpy/core/config/__init__.py b/backpy/core/config/__init__.py index 5d5535c..2a33a15 100644 --- a/backpy/core/config/__init__.py +++ b/backpy/core/config/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from backpy.core.config.configuration import TOMLConfiguration from backpy.core.config.variables import VariableLibrary diff --git a/backpy/core/config/configuration.py b/backpy/core/config/configuration.py index 779e4cb..041e1d2 100644 --- a/backpy/core/config/configuration.py +++ b/backpy/core/config/configuration.py @@ -1,6 +1,7 @@ +import tomllib from pathlib import Path -import toml +import tomli_w from backpy.core.utils.exceptions import InvalidTOMLConfigurationError @@ -38,7 +39,9 @@ def __getitem__(self, item: str): ) keys = _parse_key(item) - content_dict = toml.load(self._path) + + with open(self._path, "rb") as tomlf: + content_dict = tomllib.load(tomlf) content = content_dict for key in keys: @@ -66,13 +69,13 @@ def __setitem__(self, key: str, value: object): ) keys = _parse_key(key) - content_dict = toml.load(self._path) + with open(self._path, "rb") as tomlf: + content_dict = tomllib.load(tomlf) content = content_dict for i in range(len(keys)): key = keys[i] if i < len(keys) - 1: - if key not in content: content[key] = dict() content = content[key] @@ -87,8 +90,8 @@ def __setitem__(self, key: str, value: object): else: content[key] = value - with open(self._path, "w") as file: - toml.dump(o=content_dict, f=file) + with open(self._path, "wb") as file: + tomli_w.dump(content_dict, file) def __contains__(self, item: str): try: @@ -108,27 +111,12 @@ def create(self, create_parents: bool = True) -> None: self._path.touch(exist_ok=True) def dump_dict(self, content: dict) -> None: - with open(self._path, "w") as file: - toml.dump(o=content, f=file) + with open(self._path, "wb") as file: + tomli_w.dump(content, file) def as_dict(self) -> dict: - return toml.load(self._path) - - def prepend_comments( - self, comments: list[str] | str, linebreak: bool = True - ) -> None: - if isinstance(comments, str): - comments = [comments] - - # Adapted from https://stackoverflow.com/a/5917395 - with open(self._path, "r+") as file: - content = file.read() - file.seek(0, 0) - file.write( - "\n".join([("# " + comment) for comment in comments]) - + "\n" * (2 if linebreak else 1) - + content - ) + with open(self._path, "rb") as tomlf: + return tomllib.load(tomlf) def get_keys(self, non_dict_only: bool = False): def recursive_keys(dictionary: dict, parent: str | None = None) -> list[str]: @@ -145,17 +133,5 @@ def recursive_keys(dictionary: dict, parent: str | None = None) -> list[str]: return recursive_keys(dictionary=self.as_dict()) - def prepend_no_edit_warning(self): - self.prepend_comments( - [ - "======================================" - "======================================", - " WARNING! DO NOT EDIT THIS FILE MANUALLY! " - "THIS COULD BREAK YOUR BACKPY!", - "======================================" - "======================================", - ] - ) - def get_path(self) -> Path: return self._path diff --git a/backpy/core/config/variables.py b/backpy/core/config/variables.py index cab073e..5dfa87f 100644 --- a/backpy/core/config/variables.py +++ b/backpy/core/config/variables.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import Any from mergedeep import merge @@ -7,7 +8,6 @@ class VariableLibrary: - _instance = None def __new__(cls): @@ -61,7 +61,6 @@ def generate(self, regenerate: bool = False) -> None: self._config.dump_dict( content if regenerate else dict(merge({}, content, current_content)) ) - self._config.prepend_no_edit_warning() @classmethod def get_config(cls) -> TOMLConfiguration: @@ -74,12 +73,12 @@ def get_path(cls) -> Path: return instance._path @classmethod - def get_variable(cls, key: str): + def get_variable(cls, key: str) -> Any: instance = cls() return instance._config[key] @classmethod - def set_variable(cls, key: str, value: str) -> None: + def set_variable(cls, key: str, value: Any) -> None: instance = cls() instance._config[key] = value diff --git a/backpy/core/remote/__init__.py b/backpy/core/remote/__init__.py index 62930fe..28e55a0 100644 --- a/backpy/core/remote/__init__.py +++ b/backpy/core/remote/__init__.py @@ -1,4 +1,6 @@ +from __future__ import annotations + from backpy.core.remote import password -from backpy.core.remote.remote import Remote, Protocol +from backpy.core.remote.remote import Remote, Protocol, get_protocols -__all__ = ["password", "Remote", "Protocol"] +__all__ = ["password", "Remote", "Protocol", "get_protocols"] diff --git a/backpy/core/remote/password.py b/backpy/core/remote/password.py index 6e3af61..7ce6e50 100644 --- a/backpy/core/remote/password.py +++ b/backpy/core/remote/password.py @@ -2,6 +2,8 @@ from cryptography.fernet import Fernet +__all__ = ["encrypt", "decrypt"] + def _get_fernet() -> Fernet: key_file = Path.home() / ".backpy/config/.key" diff --git a/backpy/core/remote/remote.py b/backpy/core/remote/remote.py index e1fcd5d..044bc46 100644 --- a/backpy/core/remote/remote.py +++ b/backpy/core/remote/remote.py @@ -33,6 +33,8 @@ _DEFAULT_CONTEXT_VERBOSITY: int = 1 +__all__ = ["Remote", "Protocol", "get_protocols"] + def _calculate_hash(path: Path) -> str: with open(path, "rb") as f: @@ -54,6 +56,10 @@ def from_name(cls, name: str): return None +def get_protocols() -> list[Protocol]: + return protocols + + protocols = [ Protocol( name="scp", @@ -109,7 +115,7 @@ def __init__( self._context_managed: bool = False self._context_verbosity: int = _DEFAULT_CONTEXT_VERBOSITY - def __call__(self, context_verbosity: int, *args, **kwargs): + def __call__(self, context_verbosity: int = 1, *args, **kwargs): self._context_verbosity = context_verbosity return self @@ -122,7 +128,6 @@ def __enter__(self) -> "Remote": return self def __exit__(self, exc_type, exc_val, exc_tb) -> bool: - if not self._context_managed: return False @@ -132,7 +137,6 @@ def __exit__(self, exc_type, exc_val, exc_tb) -> bool: return False def update_config(self): - current_content = self._config.as_dict() content = { @@ -154,7 +158,6 @@ def update_config(self): self._config.dump_dict(dict(merge({}, current_content, content))) def connect(self, verbosity_level: int = 1) -> None: - if self._client is not None: self._client.close() @@ -195,7 +198,6 @@ def connect(self, verbosity_level: int = 1) -> None: print(f"Connected to {self._hostname} with user {self._username}.") def disconnect(self, verbosity_level: int = 1) -> None: - if self._client is not None: self._client.close() @@ -203,7 +205,6 @@ def disconnect(self, verbosity_level: int = 1) -> None: print(f"Connection to {self._hostname} was closed.") def test_connection(self, verbosity_level: int = 1) -> None: - if self._context_managed: raise RuntimeError( "The connection of the remote may not be tested while " @@ -247,7 +248,6 @@ def upload( max_retries: int = 3, verbosity_level: int = 1, ) -> None: - if isinstance(source, str): source = Path(source) @@ -257,7 +257,6 @@ def upload( with Progress( *Progress.get_default_columns(), DownloadColumn(), TransferSpeedColumn() ) as progress: - task = progress.add_task( f"Uploading {source.name}", visible=verbosity_level >= 1 ) @@ -306,7 +305,6 @@ def upload( for root, dirs, files in Path(source).walk( follow_symlinks=False ): - self.mkdir( target=str(target / root), parents=True, @@ -324,7 +322,6 @@ def upload( ) case "scp": - _progress = lambda filename, total, sent: progress.update( task, total=total, completed=sent ) @@ -384,7 +381,6 @@ def download( max_retries: int = 3, verbosity_level: int = 1, ) -> None: - if isinstance(target, str): target = Path(target) @@ -457,7 +453,6 @@ def mkdir( client: SFTPClient | SCPClient | None = None, verbosity_level: int = 1, ) -> None: - if not client and not self._context_managed: self.connect(verbosity_level=verbosity_level) @@ -532,7 +527,6 @@ def is_dir( sftp_client: SFTPClient | None = None, close_afterwards: bool = True, ) -> bool: - if not sftp_client and not self._context_managed: self.connect() sftp_client = self._client.open_sftp() @@ -557,7 +551,6 @@ def exists( sftp_client: SFTPClient | None = None, close_afterwards: bool = True, ) -> bool: - if not sftp_client and not self._context_managed: self.connect() sftp_client = self._client.open_sftp() @@ -583,7 +576,6 @@ def remove( close_afterwards: bool = True, verbosity_level: int = 1, ) -> None: - if not sftp_client and not self._context_managed: self.connect(verbosity_level=verbosity_level) sftp_client = self._client.open_sftp() @@ -624,8 +616,7 @@ def remove( self.disconnect(verbosity_level=verbosity_level) def delete(self, delete_files: bool, verbosity_level: int = 1): - - from backpy.core.backup.backup_space import BackupSpace + from backpy.core.space.backup_space import BackupSpace if self._context_managed: raise RuntimeError( @@ -645,7 +636,6 @@ def delete(self, delete_files: bool, verbosity_level: int = 1): space.get_remote() is not None and space.get_remote().get_uuid() == self.get_uuid() ): - if delete_files: self.remove( target=space.get_remote_path(), verbosity_level=verbosity_level @@ -672,7 +662,6 @@ def delete(self, delete_files: bool, verbosity_level: int = 1): print(f"Remote with UUID {self._uuid} was deleted.") def get_hash(self, target: str, verbosity_level: int = 1) -> str: - if not self._context_managed: self.connect(verbosity_level=verbosity_level) @@ -697,7 +686,6 @@ def get_file_size( sftp_client: SFTPClient | None = None, verbosity_level: int = 1, ) -> int: - if not sftp_client and not self._context_managed: self.connect(verbosity_level=verbosity_level) sftp_client = self._client.open_sftp() @@ -713,7 +701,6 @@ def get_file_size( return info.st_size def get_info_table(self) -> Table: - table = Table( title=f"{palette.peach}REMOTE INFORMATION{RESET}", show_header=False, @@ -776,7 +763,6 @@ def get_remotes(cls): @classmethod def load_by_uuid(cls, unique_id: str) -> "Remote": - unique_id = uuid.UUID(unique_id) config = TOMLConfiguration( @@ -859,7 +845,6 @@ def new( verbosity_level: int = 1, test_connection: bool = True, ) -> "Remote": - if name == "None": raise NameError("Remotes may not be named 'None'.") diff --git a/backpy/core/space/__init__.py b/backpy/core/space/__init__.py new file mode 100644 index 0000000..a9bdf9d --- /dev/null +++ b/backpy/core/space/__init__.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from backpy.core.space.backup_space import BackupSpace +from backpy.core.space.file_backup_space import FileBackupSpace +from backpy.core.space.types import BackupSpaceType, get_backup_space_types + +__all__ = [ + "BackupSpace", + "BackupSpaceType", + "FileBackupSpace", + "get_backup_space_types", +] diff --git a/backpy/core/backup/backup_space.py b/backpy/core/space/backup_space.py similarity index 99% rename from backpy/core/backup/backup_space.py rename to backpy/core/space/backup_space.py index 6d95d7d..c0e2857 100644 --- a/backpy/core/backup/backup_space.py +++ b/backpy/core/space/backup_space.py @@ -22,7 +22,8 @@ ) if TYPE_CHECKING: - from backpy import Backup, BackupSpaceType + from backpy.core.backup import Backup + from backpy.core.space import BackupSpaceType palette = get_default_palette() @@ -100,7 +101,7 @@ def get_backups( verbosity_level: int = 1, ) -> list[Backup]: - from backpy import Backup + from backpy.core.backup import Backup configurations = [ file if file.is_file() else None for file in self._backup_dir.glob("*.toml") @@ -426,7 +427,6 @@ def new( cls._config.create() cls.update_config() - cls._config.prepend_no_edit_warning() if cls._remote: with cls._remote(context_verbosity=verbosity_level): diff --git a/backpy/core/backup/file_backup_space.py b/backpy/core/space/file_backup_space.py similarity index 96% rename from backpy/core/backup/file_backup_space.py rename to backpy/core/space/file_backup_space.py index 4b4a18e..69ea9c2 100644 --- a/backpy/core/backup/file_backup_space.py +++ b/backpy/core/space/file_backup_space.py @@ -9,8 +9,9 @@ from rich.table import Table -from backpy.core.backup import BackupSpace, compression +from backpy.core.backup import compression from backpy.core.config import VariableLibrary +from backpy.core.space.backup_space import BackupSpace from backpy.core.utils.exceptions import ( InvalidBackupError, InvalidBackupSpaceError, @@ -18,7 +19,7 @@ ) if TYPE_CHECKING: - from backpy import Backup + from backpy.core.backup import Backup class FileBackupSpace(BackupSpace): @@ -32,7 +33,7 @@ def create_backup( verbosity_level: int = 1, ) -> Backup: - from backpy import Backup + from backpy.core.backup import Backup if exclude is None: exclude = [] @@ -59,7 +60,7 @@ def restore_backup( verbosity_level: int = 1, ) -> None: - from backpy import Backup + from backpy.core.backup import Backup backup = Backup.load_by_uuid( backup_space=self, @@ -229,7 +230,7 @@ def new( **kwargs, ) -> "FileBackupSpace": - from backpy import BackupSpaceType + from backpy.core.space import BackupSpaceType parent = super(FileBackupSpace, cls).new( name=name, space_type=BackupSpaceType.from_name("FILE_SYSTEM"), **kwargs diff --git a/backpy/core/backup/types.py b/backpy/core/space/types.py similarity index 80% rename from backpy/core/backup/types.py rename to backpy/core/space/types.py index 29e348f..ad0acdc 100644 --- a/backpy/core/backup/types.py +++ b/backpy/core/space/types.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Type if TYPE_CHECKING: - from backpy import BackupSpace + from backpy.core.space import BackupSpace @dataclass @@ -18,14 +18,14 @@ class BackupSpaceType: @classmethod def from_name(cls, name): - for backup_space_type in _get_backups_space_type(): + for backup_space_type in get_backup_space_types(): if backup_space_type.name == name: return backup_space_type return None -def _get_backups_space_type(): - from .file_backup_space import FileBackupSpace +def get_backup_space_types() -> list[BackupSpaceType]: + from backpy.core.space.file_backup_space import FileBackupSpace return [ # BackupSpaceType( diff --git a/backpy/core/utils/__init__.py b/backpy/core/utils/__init__.py index 88cc39b..28e6605 100644 --- a/backpy/core/utils/__init__.py +++ b/backpy/core/utils/__init__.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from backpy.core.utils import exceptions from backpy.core.utils.times import TimeObject -from backpy.core.utils.utils import bytes2str, calculate_sha256sum +from backpy.core.utils.utils import bytes2str, str2bytes, calculate_sha256sum -__all__ = ["exceptions", "TimeObject", "bytes2str", "calculate_sha256sum"] +__all__ = ["exceptions", "TimeObject", "bytes2str", "str2bytes", "calculate_sha256sum"] diff --git a/docs/Makefile b/docs/Makefile index d0c3cbf..d4bb2cb 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -5,8 +5,8 @@ # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build +SOURCEDIR = . +BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: diff --git a/docs/_extensions/no_ansi.py b/docs/_extensions/no_ansi.py new file mode 100644 index 0000000..f504b08 --- /dev/null +++ b/docs/_extensions/no_ansi.py @@ -0,0 +1,39 @@ +# Transparency notice: +# A substantial amount of this extension's code was generated +# by the generative AI ChatGPT 5.1 and modified by the +# developer. + +import re + +from docutils import nodes +from sphinx.application import Sphinx + +COLOR_RE = re.compile(r"\x1b\[38;2;\d+;\d+;\d+m") +ANSI_RE = re.compile(r"\x1b\[[0-9;]*m") + + +def clean_ansi(text: str) -> str: + text = COLOR_RE.sub("", text) + return ANSI_RE.sub("", text) + + +def strip_ansi_from_nodes(node): + for child in node.traverse(nodes.Text): + original = child.astext() + cleaned = clean_ansi(original) + + if cleaned != original: + child.parent.replace(child, nodes.Text(cleaned)) + + +def on_doctree_resolved(app, doctree, docname): + strip_ansi_from_nodes(doctree) + + +def setup(app: Sphinx): + app.connect("doctree-resolved", on_doctree_resolved) + return { + "version": "2.0", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 0000000..d86b033 --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,38 @@ +/* + Transparency notice: + A substantial amount of this CSS code was generated + by the generative AI Claude Sonnet 4.5 and modified by the + developer. +*/ + +div.sd-card { + border-radius: 10px; +} + +div.sd-card.principle { + border-left: 3px solid #4a4a95; +} + +div.sd-card.phase-1 { + border-left: 4px solid #d20f39; +} + +div.sd-card.phase-2 { + border-left: 4px solid #e64553; +} + +div.sd-card.phase-3 { + border-left: 4px solid #df8e1d; +} + +div.sd-card.phase-4 { + border-left: 4px solid #df8e1d; +} + +div.sd-card.phase-5 { + border-left: 4px solid #40a02b; +} + +div.sd-card.phase-6 { + border-left: 4px solid #7287fd; +} diff --git a/docs/_static/fonts/fira_sans/FiraSans-Bold.ttf b/docs/_static/fonts/fira_sans/FiraSans-Bold.ttf new file mode 100644 index 0000000..e3593fb Binary files /dev/null and b/docs/_static/fonts/fira_sans/FiraSans-Bold.ttf differ diff --git a/docs/_static/fonts/fira_sans/FiraSans-BoldItalic.ttf b/docs/_static/fonts/fira_sans/FiraSans-BoldItalic.ttf new file mode 100644 index 0000000..305b0b8 Binary files /dev/null and b/docs/_static/fonts/fira_sans/FiraSans-BoldItalic.ttf differ diff --git a/docs/_static/fonts/fira_sans/FiraSans-Italic.ttf b/docs/_static/fonts/fira_sans/FiraSans-Italic.ttf new file mode 100644 index 0000000..27d32ed Binary files /dev/null and b/docs/_static/fonts/fira_sans/FiraSans-Italic.ttf differ diff --git a/docs/_static/fonts/fira_sans/FiraSans-Regular.ttf b/docs/_static/fonts/fira_sans/FiraSans-Regular.ttf new file mode 100644 index 0000000..6f80647 Binary files /dev/null and b/docs/_static/fonts/fira_sans/FiraSans-Regular.ttf differ diff --git a/docs/_static/fonts/jetbrains_mono/JetBrainsMono-Regular.ttf b/docs/_static/fonts/jetbrains_mono/JetBrainsMono-Regular.ttf new file mode 100644 index 0000000..436c982 Binary files /dev/null and b/docs/_static/fonts/jetbrains_mono/JetBrainsMono-Regular.ttf differ diff --git a/docs/_static/logos/backpy_header_dark.png b/docs/_static/logos/backpy_header_dark.png new file mode 100644 index 0000000..a354bb7 Binary files /dev/null and b/docs/_static/logos/backpy_header_dark.png differ diff --git a/docs/_static/logos/backpy_header_light.png b/docs/_static/logos/backpy_header_light.png new file mode 100644 index 0000000..5e5de5b Binary files /dev/null and b/docs/_static/logos/backpy_header_light.png differ diff --git a/docs/_static/logos/backpy_icon_dark.png b/docs/_static/logos/backpy_icon_dark.png new file mode 100644 index 0000000..a399b11 Binary files /dev/null and b/docs/_static/logos/backpy_icon_dark.png differ diff --git a/docs/_static/logos/backpy_icon_light.png b/docs/_static/logos/backpy_icon_light.png new file mode 100644 index 0000000..1ec3a3e Binary files /dev/null and b/docs/_static/logos/backpy_icon_light.png differ diff --git a/docs/_static/logos/backpy_logo_dark.png b/docs/_static/logos/backpy_logo_dark.png new file mode 100644 index 0000000..8c32a0d Binary files /dev/null and b/docs/_static/logos/backpy_logo_dark.png differ diff --git a/docs/_static/logos/backpy_logo_light.png b/docs/_static/logos/backpy_logo_light.png new file mode 100644 index 0000000..2a90639 Binary files /dev/null and b/docs/_static/logos/backpy_logo_light.png differ diff --git a/docs/_templates/partials/webfonts.html b/docs/_templates/partials/webfonts.html new file mode 100644 index 0000000..8aecdee --- /dev/null +++ b/docs/_templates/partials/webfonts.html @@ -0,0 +1,51 @@ + diff --git a/docs/about/acknowledgements.rst b/docs/about/acknowledgements.rst new file mode 100644 index 0000000..de40db6 --- /dev/null +++ b/docs/about/acknowledgements.rst @@ -0,0 +1,21 @@ +.. _acknowledgements: + +**************** +Acknowledgements +**************** + + +Documentation +------------- + +This documentation is partially inspired by the documentation of +the documentations of the Python packages `pyvisgen `_, +`astropy `_. + +While no substantial code segments were adapted, this documentation makes use of +packages and tools (e.g. ``automodapi`` or ``towncrier``) which are also used +in these projects. +Especially the page structure of the :ref:`api-reference`'s source code, is inspired by +the `pyvisgen `_ package. + +All of their licenses are listed on our :ref:`license` page. diff --git a/docs/about/changelog.rst b/docs/about/changelog.rst new file mode 100644 index 0000000..30a8362 --- /dev/null +++ b/docs/about/changelog.rst @@ -0,0 +1,7 @@ +.. _changelog: + +********* +Changelog +********* + +.. include:: ../changes/CHANGELOG.rst diff --git a/docs/about/index.rst b/docs/about/index.rst new file mode 100644 index 0000000..778a029 --- /dev/null +++ b/docs/about/index.rst @@ -0,0 +1,56 @@ +.. _about: + +**************** +About ``backpy`` +**************** + +.. grid:: 2 + :class-row: surface + + .. grid-item-card:: :octicon:`project-roadmap` Roadmap + :link: roadmap + :link-type: ref + + Want to know what is planned for the future? Find out here! + + .. grid-item-card:: :octicon:`law` License + :link: license + :link-type: ref + + Here you find the license for backpy and its assets. + +.. grid:: 2 + :class-row: surface + + .. grid-item-card:: :octicon:`heart` Acknowledgements + :link: acknowledgements + :link-type: ref + + Here you can find the acknowledgements of other works + that inspired parts of this project. + + .. grid-item-card:: :iconify:`mingcute:ai-line` Usage of Generative AI + :link: use-of-gen-ai + :link-type: ref + + Here you can find a statement and several principles about the usage of generative + artificial intelligence in this project. + +.. grid:: 1 + :class-row: surface + + .. grid-item-card:: :octicon:`history` Changelog + :link: changelog + :link-type: ref + + Here you can track the changes of the package over the course + of time. + +.. toctree:: + :hidden: + + roadmap + license + acknowledgements + usage-of-generative-ai + changelog diff --git a/docs/about/license.rst b/docs/about/license.rst new file mode 100644 index 0000000..cf2dcc8 --- /dev/null +++ b/docs/about/license.rst @@ -0,0 +1,13 @@ +.. _license: + +******* +License +******* + +Main License +------------ + +.. include:: ../../LICENSE + :literal: + +.. include:: ../../ASSETS_LEGAL.rst diff --git a/docs/about/roadmap.rst b/docs/about/roadmap.rst new file mode 100644 index 0000000..70c9a3d --- /dev/null +++ b/docs/about/roadmap.rst @@ -0,0 +1,56 @@ +.. _roadmap: + +******* +Roadmap +******* + +.. card:: + :class-card: phase-1 + + **Phase 1 (pre-release)** + ^^^ + - Documentation for API and CLI (via docstrings and documentation website) + - User-guides and troubleshooting pages for documentation + - Publish hosted version of documentation + + +.. card:: + :class-card: phase-2 + + **Phase 2 (pre-release)** + ^^^ + - MariaDB / MySQL-database backups + - Pytest tests and GitHub CI (mainly for on Linux-based systems) + +.. card:: + :class-card: phase-3 + + **Phase 3 (pre-release)** + ^^^ + - Publish package on PyPI + - Publish package in APT repository + +.. card:: + :class-card: phase-4 + + **Phase 4** + ^^^ + - Encryption for saved backup-archives + - Migration and export of backpy backups + - Status notifications (e.g. e-mail or SMS if backups fail) + - Logging for manual and automatic backups + +.. card:: + :class-card: phase-5 + + **Phase 5** + ^^^ + - Encryption for saved backup-archives + +.. card:: + :class-card: phase-6 + + **Phase 6 (aka. the "maybe" list)** + ^^^ + - Webinterface for monitoring backups + - Webinterface for interacting with backpy \ No newline at end of file diff --git a/docs/about/usage-of-generative-ai.rst b/docs/about/usage-of-generative-ai.rst new file mode 100644 index 0000000..27a5924 --- /dev/null +++ b/docs/about/usage-of-generative-ai.rst @@ -0,0 +1,98 @@ +.. _use-of-gen-ai: + +********************** +Usage of Generative AI +********************** + +Statement and Principles +------------------------ + +The usage of generative artificial intelligence ("GenAI") in programming has the potential to optimize the workflows +of developers, assist in debugging and testing programs and generating repetitive code. However there are inherent risks +in using GenAI without proper human supervision. +Most importantly, the ethical justifiability of using GenAI in coding is highly dependent on the transparency and responsible +behavior of the developers. [1]_ + +In this project, the following principles were applied regarding the usage of GenAI for coding: + +.. card:: + :class-card: principle + + **1st Principle** + ^^^ + If GenAI is used for generating **substantial amounts of code** (e.g. multiple lines, entire functions, classes + or files) a transparency notice has to be provided stating that + + * GenAI was used to generate the code, + * specifying which parts of the code were generated + * using which model and + * whether the code was modified by human developers. + + In any case the usage must be permitted by the *Terms of Use* (or comparable legal usage requirements) + of the used model and may not violate any current laws. If the *Terms of Use* require additional steps to + allow the usage of the code, these must be met or the code may not be used. + +.. card:: + :class-card: principle + + **2nd Principle** + ^^^ + + Since GenAI is subject to complications like biases, hallucinations and is highly dependent on the given input, + generated contents should never be used without checking the content manually. Publishing generated code without + review poses potential risks for the stability of the program and the devices used to run it. + +.. card:: + :class-card: principle + + **3rd Principle** + ^^^ + + There are certain situations in which using GenAI can be advantageous and there are situations in which + it should not be used. + In general, using GenAI should not be first solution when encountering problems. + It is a tool to help the developers but should not replace critical and analytic thinking and problem + solving. + + This project generally tries to apply the following rule of thumb for some of the most important examples: + + .. table:: Sensible vs Not Sensible - Using GenAI in Programming + + ====================================================== ========================================== + Sensible Not Sensible + ====================================================== ========================================== + Generating boilerplate / repetitive code Generating large algorithms + Additional debugging checks of code Generating entire parts of the codebase + Test development (primarily for checking edge cases) Creating safety critical parts of the code + Documenting code Documenting code + ====================================================== ========================================== + + + .. note:: + + The aspect of *Documenting code* appears in both columns of the table above since this is a debatable application + of GenAI and **should be decided from case to case**. + + On one hand documenting similar functions and wrappers is repetitive and time consuming, so it could make + sense to save time by generating this (especially using in-line completion tools). On the other hand, the person writing + the code should be the one to document it since he/she knows the functionality of the code best and therefore + should be able explain it. + +Example: Transparency Notice +---------------------------- + +.. code:: txt + + Transparency notice + ------------------- + + A substantial amount of this extension's code generated + by the generative AI and modified by the + developer. + + + +References and Further Reading +------------------------------ + +.. [1] Atemkeng, M., Hamlomo, S., Welman, B., Oyetunji, N., Ataei, P., and E Fendji, J. L. K., “Ethics of Software Programming with Generative AI: Is Programming without Generative AI always radical?”, 2024. doi:`10.48550/arXiv.2408.10554 `_. diff --git a/docs/changes/tutorial-towncrier.rst b/docs/changes/tutorial-towncrier.rst new file mode 100644 index 0000000..15df174 --- /dev/null +++ b/docs/changes/tutorial-towncrier.rst @@ -0,0 +1,81 @@ +---------------------------------- +Tutorial: How to use ``towncrier`` +---------------------------------- + +We use the python package ``towncrier`` to manage our changelogs. +It can be used to create changelog files (e.g. for pull requests). +When building the docs, these files are collected and merged into +a ``changelog.rst`` file containing the entire changelog of the project. + +Therefore it is mandatory to create changelogs when creating pull requests. +There are two main ways to create a ``towncrier`` changelog. + +Variant 1: Using the ``towncrier`` CLI +-------------------------------------- + +The guided way to generate changelogs, is the CLI of ``towncrier``. +To be able to use it, you have to install the ``backpy`` package for development. +Make sure you also install the optional dependencies ``docs``. + +After just navigate to your local backpy git repository. +There you can enter the following command + +.. code-block:: bash + + towncrier create + +You will be prompted to enter the ``issue number``. +Enter the number of the **pull request** your changes are included in. + +.. code-block:: + + > Issue number (`+` if none): █ + +Then you have to enter the type of the changes: +Choose the appropriate label for your changes. If you have changes with different types, **create a changelog for every change**. + +.. code-block:: + + > Fragment type (api, cli, docs, bugfix): █ + +Now a text editor should open where you can specify your changes. +Keep the description short and concise. You can use sphinx's ``rst`` syntax. + + **Tip:** Refer to this cheat sheet to look up possible ``rst`` commands: https://sphinx-tutorial.readthedocs.io/cheatsheet/ + +Finally, save your document and commit it to the branch you created the pull request for. + +Variant 2: Manually Creating Files +---------------------------------- + +The shortest way to create your changelog files is creating them yourself. +Therefore navigate to your local ``backpy`` repository. +Inside the repository navigate to ``docs/changes``. + +.. code-block:: + + cd docs/changes + +Create a file with the following naming scheme: + +.. code-block:: + + ..rst + +The possible types are: + +============ ======================= +``api`` API Changes +``cli`` CLI Changes +``docs`` Documentation Changes +``bugfix`` Bug Fixes +============ ======================= + +Open the created file with a editor of your choice and document your changes. +Keep the description short and concise. You can use sphinx's ``rst`` syntax. + + **Tip:** Refer to this cheat sheet to look up possible ``rst`` commands: https://sphinx-tutorial.readthedocs.io/cheatsheet/ + +Finally, save your document and commit it to the branch you created the pull request for. + + diff --git a/docs/concepts/backup-spaces.rst b/docs/concepts/backup-spaces.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/concepts/backups-and-restore.rst b/docs/concepts/backups-and-restore.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/concepts/encryption.rst b/docs/concepts/encryption.rst new file mode 100644 index 0000000..2faa9e6 --- /dev/null +++ b/docs/concepts/encryption.rst @@ -0,0 +1,6 @@ +.. _concepts_encryption: + +********** +Encryption +********** + diff --git a/docs/concepts/include-exclude.rst b/docs/concepts/include-exclude.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/concepts/index.rst b/docs/concepts/index.rst new file mode 100644 index 0000000..f6f081d --- /dev/null +++ b/docs/concepts/index.rst @@ -0,0 +1,8 @@ +.. _concepts: + +************** +Basic Concepts +************** + +The following pages contain several guides + diff --git a/docs/concepts/limits-and-auto-deletion.rst b/docs/concepts/limits-and-auto-deletion.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/concepts/local-vs-remote.rst b/docs/concepts/local-vs-remote.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/concepts/schedules.rst b/docs/concepts/schedules.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..26a05a7 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,84 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import sys +from datetime import datetime +from pathlib import Path + +import backpy.version +from backpy import TOMLConfiguration + +pyproject = TOMLConfiguration(Path(backpy.__file__).parent.parent / "pyproject.toml") +authors = pyproject["project.authors"] +authors = ",".join([author["name"] for author in authors]) +year = datetime.now().year + +version = backpy.version.version + +project = "backpy" +copyright = f"© {year}, {authors}" +author = authors +version = version +release = version + +rst_prolog = f""" +.. |version| replace:: ``{version}`` +""" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +sys.path.append(str(Path("_extensions").resolve())) + +extensions = [ + "sphinx_iconify", + "sphinx_design", + "sphinx_copybutton", + "sphinx_togglebutton", + "sphinx_click", + "sphinx_automodapi.automodapi", + "numpydoc", + "no_ansi", +] + +autosummary_generate = True +autodoc_typehints = "description" +numpydoc_show_class_members = False + +automodapi_toctreedirnm = "_generated" +automodapi_inheritance_diagram = False + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# -- Options for HTMLoutput ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "shibuya" +html_static_path = ["_static"] +html_css_files = ["custom.css"] + +html_favicon = "_static/logos/backpy_icon_dark.png" + +html_theme_options = { + "light_logo": "_static/logos/backpy_header_light.png", + "dark_logo": "_static/logos/backpy_header_dark.png", + "github_url": "https://github.com/tgross03/backpy", + "accent_color": "blue", + "announcement": "This package is still in development and not stable at this time! " + "Features and functionalities might not work as expected.", + "show_ai_links": False, +} + +html_context = { + "source_type": "github", + "source_user": "tgross03", + "source_repo": "backpy", + "source_version": "main", # Optional + "source_docs_path": "/docs/", # Optional +} diff --git a/docs/getting-started/index.rst b/docs/getting-started/index.rst new file mode 100644 index 0000000..891c99d --- /dev/null +++ b/docs/getting-started/index.rst @@ -0,0 +1,31 @@ +.. _getting-started: + +*************** +Getting Started +*************** + +You are new to ``backpy`` or want to read up on the first steps again? +Then you are at the right place. + +.. grid:: 2 + :class-row: surface + + .. grid-item-card:: :octicon:`move-to-bottom` Installation + :link: installation + :link-type: ref + + Here you can read up on the ways to install backpy + on your system. + + .. grid-item-card:: :octicon:`goal` First Steps + :link: quickstart + :link-type: ref + + This short guide quickly describes the first steps + to getting started with backpy. + +.. toctree:: + :hidden: + + installation + quickstart \ No newline at end of file diff --git a/docs/getting-started/installation.rst b/docs/getting-started/installation.rst new file mode 100644 index 0000000..ceead06 --- /dev/null +++ b/docs/getting-started/installation.rst @@ -0,0 +1,5 @@ +.. _installation: + +************ +Installation +************ diff --git a/docs/getting-started/quickstart.rst b/docs/getting-started/quickstart.rst new file mode 100644 index 0000000..d2c8745 --- /dev/null +++ b/docs/getting-started/quickstart.rst @@ -0,0 +1,11 @@ +.. _quickstart: + +*********** +First Steps +*********** + +.. _interactive-mode: +*Interactive Mode* +------------------ + +The ``backpy`` CLI contains many different subcommands \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..a26bc63 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,83 @@ +.. _backpy: + +.. image:: /_static/logos/backpy_logo_light.png + :width: 100% + :align: center + :class: light-only + :alt: Insert backpy logo here + +.. image:: /_static/logos/backpy_logo_dark.png + :width: 100% + :align: center + :class: dark-only + :alt: Insert backpy logo here + +**Version:** |version| + +``backpy`` is a Python-based package for creating backups of files, directories and +SQL databases. It features an extensive and well-documented Command-Line Interface (CLI) +to interact with the system and also an easy-to-use Python-API. + +.. warning:: + This package is still in development and not stable at this time! + Features and functionalities might not work as expected. + +Useful links +------------ + +.. grid:: 3 + :class-row: surface + + .. grid-item-card:: :octicon:`zap` Getting Started + :link: getting-started + :link-type: ref + + New to backpy? This is the place you want to go to! + + .. grid-item-card:: :octicon:`bug` Troubleshooting + :link: troubleshooting + :link-type: ref + + Having troubles with the package? You might find an answer here! + + .. grid-item-card:: :octicon:`git-compare` Workflows + :link: workflows + :link-type: ref + + Here you can find guides for typical workflows for using backpy. + +.. grid:: 3 + :class-row: surface + + .. grid-item-card:: :octicon:`repo` Basic Concepts + :link: concepts + :link-type: ref + + Want to learn more about the structure of backpy? + Then you should go here. + + .. grid-item-card:: :octicon:`gear` CLI / API Reference + :link: reference + :link-type: ref + + Here you can find the full references for the CLI and API + of backpy. + + .. grid-item-card:: :octicon:`question` About + :link: about + :link-type: ref + + Here get to know more about the developers, package and legal stuff. + + +.. toctree:: + :includehidden: + :hidden: + + getting-started/index + concepts/index + workflows/index + troubleshooting/index + reference/index + about/index + diff --git a/docs/make.bat b/docs/make.bat index 747ffb7..32bb245 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -7,8 +7,8 @@ REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) -set SOURCEDIR=source -set BUILDDIR=build +set SOURCEDIR=. +set BUILDDIR=_build %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( diff --git a/docs/reference/api/cli/colors.rst b/docs/reference/api/cli/colors.rst new file mode 100644 index 0000000..99f1f45 --- /dev/null +++ b/docs/reference/api/cli/colors.rst @@ -0,0 +1,14 @@ +.. _api-cli-colors: + +****************************** +Colors (``backpy.cli.colors``) +****************************** + +.. currentmodule:: backpy.cli.colors + +The :mod:`backpy.cli.colors` submodule contains the color palettes used in the :ref:`CLI `. +It uses the color palettes from `Catppuccin `_ and translates them into ANSI codes +that can be displayed as colors in the terminal. + +.. automodapi:: backpy.cli.colors + :no-heading: diff --git a/docs/reference/api/cli/elements.rst b/docs/reference/api/cli/elements.rst new file mode 100644 index 0000000..2d9f93c --- /dev/null +++ b/docs/reference/api/cli/elements.rst @@ -0,0 +1,17 @@ +.. _api-cli-elements: + +********************************** +Elements (``backpy.cli.elements``) +********************************** + +.. currentmodule:: backpy.cli.elements + +The :mod:`backpy.cli.elements` submodule contains functions and classes +used to interact with the :ref:`CLI `. + +The contained classes are used to prompt the user to enter values (e.g. in :ref:`interactive-mode`) or +to confirm a command. + + +.. automodapi:: backpy.cli.elements + :no-heading: diff --git a/docs/reference/api/cli/index.rst b/docs/reference/api/cli/index.rst new file mode 100644 index 0000000..21bf5e4 --- /dev/null +++ b/docs/reference/api/cli/index.rst @@ -0,0 +1,19 @@ +.. _api-cli: + +******************** +CLI (``backpy.cli``) +******************** + +.. currentmodule:: backpy.cli + +The :mod:`backpy.cli` module contains functions and classes +used to interact with the :ref:`CLI `. + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + colors + elements diff --git a/docs/reference/api/core/backup/compression.rst b/docs/reference/api/core/backup/compression.rst new file mode 100644 index 0000000..91ce39c --- /dev/null +++ b/docs/reference/api/core/backup/compression.rst @@ -0,0 +1,13 @@ +.. _api-backup-compression: + +************************************************ +Compression (``backpy.core.backup.compression``) +************************************************ + +.. currentmodule:: backpy.core.backup.compression + +The :mod:`backpy.core.backup.compression` submodule contains functions and classes +to deal with file compression. + +.. automodapi:: backpy.core.backup.compression + :no-heading: diff --git a/docs/reference/api/core/backup/index.rst b/docs/reference/api/core/backup/index.rst new file mode 100644 index 0000000..7162273 --- /dev/null +++ b/docs/reference/api/core/backup/index.rst @@ -0,0 +1,21 @@ +.. _api-backup: + +******************************* +Backup (``backpy.core.backup``) +******************************* + +.. currentmodule:: backpy.core.backup + +The :mod:`backpy.core.backup` submodule contains classes dealing with the manual and +automatic creation and management of backups. + +Submodules +^^^^^^^^^^ + +.. toctree:: + :maxdepth: 1 + + compression + +.. automodapi:: backpy.core.backup + :no-heading: diff --git a/docs/reference/api/core/config/index.rst b/docs/reference/api/core/config/index.rst new file mode 100644 index 0000000..4d7af0e --- /dev/null +++ b/docs/reference/api/core/config/index.rst @@ -0,0 +1,19 @@ +.. _api-config: + +******************************* +Config (``backpy.core.config``) +******************************* + +.. currentmodule:: backpy.core.config + +The :mod:`backpy.core.config` submodule contains classes to enable the management +of configuration files for many applications like global configurations, backup +configurations and many more. + +.. toctree:: + :maxdepth: 1 + + compression + +.. automodapi:: backpy.core.config + :no-heading: diff --git a/docs/reference/api/core/index.rst b/docs/reference/api/core/index.rst new file mode 100644 index 0000000..b1fc0ec --- /dev/null +++ b/docs/reference/api/core/index.rst @@ -0,0 +1,18 @@ +.. _api-core: + +********************** +Core (``backpy.core``) +********************** + +.. currentmodule:: backpy.core + +The :mod:`backpy.core` contains the core functions of ``backpy``. + +Submodules +---------- + +.. toctree:: + :glob: + :maxdepth: 2 + + */index diff --git a/docs/reference/api/core/remote/index.rst b/docs/reference/api/core/remote/index.rst new file mode 100644 index 0000000..7304367 --- /dev/null +++ b/docs/reference/api/core/remote/index.rst @@ -0,0 +1,24 @@ +.. _api-remote: + +******************************* +Remote (``backpy.core.remote``) +******************************* + +.. currentmodule:: backpy.core.remote + +The :mod:`backpy.core.remote` submodule contains classes to enable the management +of configuration files for many applications like global configurations, backup +configurations and many more. + +Submodules +^^^^^^^^^^ + +.. toctree:: + :maxdepth: 1 + + password + + +.. automodapi:: backpy.core.remote + :no-heading: + :include-all-objects: diff --git a/docs/reference/api/core/remote/password.rst b/docs/reference/api/core/remote/password.rst new file mode 100644 index 0000000..d17e9ab --- /dev/null +++ b/docs/reference/api/core/remote/password.rst @@ -0,0 +1,15 @@ +.. _api-remote-password: + +****************************************** +Password (``backpy.core.remote.password``) +****************************************** + +.. currentmodule:: backpy.core.remote.password + +The :mod:`backpy.core.remote.password` submodule contains functions to handle encryption +and decryption of passwords and passphrases so that they can be stored without immediate +human readablity. + + +.. automodapi:: backpy.core.remote.password + :no-heading: diff --git a/docs/reference/api/core/space/index.rst b/docs/reference/api/core/space/index.rst new file mode 100644 index 0000000..8eeb377 --- /dev/null +++ b/docs/reference/api/core/space/index.rst @@ -0,0 +1,14 @@ +.. _api-space: + +***************************** +Space (``backpy.core.space``) +***************************** + +.. currentmodule:: backpy.core.space + +The :mod:`backpy.core.space` submodule contains classes and functions to manage +different types of backup spaces. + +.. automodapi:: backpy.core.space + :no-heading: + :include-all-objects: diff --git a/docs/reference/api/core/utils/exceptions.rst b/docs/reference/api/core/utils/exceptions.rst new file mode 100644 index 0000000..e76819c --- /dev/null +++ b/docs/reference/api/core/utils/exceptions.rst @@ -0,0 +1,14 @@ +.. _api-utils-exceptions: + +********************************************* +Exceptions (``backpy.core.utils.exceptions``) +********************************************* + +.. currentmodule:: backpy.core.utils.exceptions + +The :mod:`backpy.core.utils.exceptions` contains the exceptions used in +the API and CLI of the package. + +.. automodapi:: backpy.core.utils.exceptions + :no-heading: + :include-all-objects: diff --git a/docs/reference/api/core/utils/index.rst b/docs/reference/api/core/utils/index.rst new file mode 100644 index 0000000..5df23c7 --- /dev/null +++ b/docs/reference/api/core/utils/index.rst @@ -0,0 +1,22 @@ +.. _api-utils: + +***************************** +Utils (``backpy.core.utils``) +***************************** + +.. currentmodule:: backpy.core.utils + +The :mod:`backpy.core.utils` submodule contains classes and functions to manage +different types of backup spaces. + +Submodules +^^^^^^^^^^ + +.. toctree:: + :maxdepth: 1 + + exceptions + +.. automodapi:: backpy.core.utils + :no-heading: + :include-all-objects: diff --git a/docs/reference/api/index.rst b/docs/reference/api/index.rst new file mode 100644 index 0000000..b792d9a --- /dev/null +++ b/docs/reference/api/index.rst @@ -0,0 +1,11 @@ +.. _api-reference: + +************* +API Reference +************* + +.. toctree:: + :maxdepth: 2 + + cli/index + core/index diff --git a/docs/reference/cli/backup.rst b/docs/reference/cli/backup.rst new file mode 100644 index 0000000..8235ce8 --- /dev/null +++ b/docs/reference/cli/backup.rst @@ -0,0 +1,9 @@ +.. _cli-backup: + +********** +``backup`` +********** + +.. click:: backpy.cli.backup.commands:command + :prog: backpy backup + :nested: full \ No newline at end of file diff --git a/docs/reference/cli/config.rst b/docs/reference/cli/config.rst new file mode 100644 index 0000000..0a73322 --- /dev/null +++ b/docs/reference/cli/config.rst @@ -0,0 +1,9 @@ +.. _cli-config: + +********** +``config`` +********** + +.. click:: backpy.cli.config.commands:command + :prog: backpy config + :nested: full \ No newline at end of file diff --git a/docs/reference/cli/index.rst b/docs/reference/cli/index.rst new file mode 100644 index 0000000..1825b36 --- /dev/null +++ b/docs/reference/cli/index.rst @@ -0,0 +1,70 @@ +.. _cli-reference: + +************* +CLI Reference +************* + + +The ``backpy`` CLI uses one general command: + +.. code-block:: + + backpy [OPTIONS] COMMAND [ARGS] ... + +Options +------- + +--version, -v Displays the current version of backpy. + +--info Displays some information about backpy. + +Subcommands +----------- + +.. grid:: 2 + :class-row: surface + + .. grid-item-card:: :octicon:`file-zip` ``backup`` + :link: cli-backup + :link-type: ref + + Actions related to creating and managing backups. + + .. grid-item-card:: :octicon:`sliders` ``config`` + :link: cli-config + :link-type: ref + + Actions related to configuring the package. + +.. grid:: 2 + :class-row: surface + + .. grid-item-card:: :octicon:`server` ``remote`` + :link: cli-remote + :link-type: ref + + Actions related to remote locations to save backups at. + + .. grid-item-card:: :octicon:`clock` ``schedule`` + :link: cli-schedule + :link-type: ref + + Actions related to scheduling for automatic backups. + +.. grid:: 1 + :class-row: surface + + .. grid-item-card:: :octicon:`archive` ``space`` + :link: cli-space + :link-type: ref + + Actions related to creating and managing backup spaces. + +.. toctree:: + :hidden: + + backup + config + remote + schedule + space \ No newline at end of file diff --git a/docs/reference/cli/remote.rst b/docs/reference/cli/remote.rst new file mode 100644 index 0000000..bacffbb --- /dev/null +++ b/docs/reference/cli/remote.rst @@ -0,0 +1,9 @@ +.. _cli-remote: + +********** +``remote`` +********** + +.. click:: backpy.cli.remote.commands:command + :prog: backpy remote + :nested: full \ No newline at end of file diff --git a/docs/reference/cli/schedule.rst b/docs/reference/cli/schedule.rst new file mode 100644 index 0000000..1105b2b --- /dev/null +++ b/docs/reference/cli/schedule.rst @@ -0,0 +1,9 @@ +.. _cli-schedule: + +************ +``schedule`` +************ + +.. click:: backpy.cli.schedule.commands:command + :prog: backpy schedule + :nested: full \ No newline at end of file diff --git a/docs/reference/cli/space.rst b/docs/reference/cli/space.rst new file mode 100644 index 0000000..d2d6056 --- /dev/null +++ b/docs/reference/cli/space.rst @@ -0,0 +1,9 @@ +.. _cli-space: + +********* +``space`` +********* + +.. click:: backpy.cli.space.commands:command + :prog: backpy space + :nested: full \ No newline at end of file diff --git a/docs/reference/index.rst b/docs/reference/index.rst new file mode 100644 index 0000000..c84c3d3 --- /dev/null +++ b/docs/reference/index.rst @@ -0,0 +1,25 @@ +.. _reference: + +********* +Reference +********* + +The following references give a technical overview for the API and CLI of ``backpy``. + +.. grid:: 2 + :class-row: surface + + .. grid-item-card:: :octicon:`command-palette` CLI Reference + :link: cli-reference + :link-type: ref + + .. grid-item-card:: :octicon:`code-square` API Reference + :link: api-reference + :link-type: ref + + +.. toctree:: + :hidden: + + api/index + cli/index \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 2b1702e..0000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,26 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - -project = "backpy" -copyright = "2025, Tom Groß" -author = "Tom Groß" - -# -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - -extensions = [] - -templates_path = ["_templates"] -exclude_patterns = [] - - -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - -html_theme = "alabaster" -html_static_path = ["_static"] diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index db03ba6..0000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. backpy documentation master file, created by - sphinx-quickstart on Wed Dec 10 22:28:05 2025. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -backpy documentation -==================== - -Add your content using ``reStructuredText`` syntax. See the -`reStructuredText `_ -documentation for details. - - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - diff --git a/docs/troubleshooting/index.rst b/docs/troubleshooting/index.rst new file mode 100644 index 0000000..daaba30 --- /dev/null +++ b/docs/troubleshooting/index.rst @@ -0,0 +1,6 @@ +.. _troubleshooting: + +*************** +Troubleshooting +*************** + diff --git a/docs/workflows/automation/creating-schedules.rst b/docs/workflows/automation/creating-schedules.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/workflows/automation/index.rst b/docs/workflows/automation/index.rst new file mode 100644 index 0000000..b114173 --- /dev/null +++ b/docs/workflows/automation/index.rst @@ -0,0 +1,5 @@ +.. _workflows-automation: + +********** +Automation +********** \ No newline at end of file diff --git a/docs/workflows/automation/manage-schedules.rst b/docs/workflows/automation/manage-schedules.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/workflows/common/creating-backups.rst b/docs/workflows/common/creating-backups.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/workflows/common/index.rst b/docs/workflows/common/index.rst new file mode 100644 index 0000000..6644904 --- /dev/null +++ b/docs/workflows/common/index.rst @@ -0,0 +1,5 @@ +.. _workflows-common: + +****** +Common +****** diff --git a/docs/workflows/common/locking-backups.rst b/docs/workflows/common/locking-backups.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/workflows/common/restore-backups.rst b/docs/workflows/common/restore-backups.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/workflows/index.rst b/docs/workflows/index.rst new file mode 100644 index 0000000..f0c80f9 --- /dev/null +++ b/docs/workflows/index.rst @@ -0,0 +1,53 @@ +.. _workflows: + +********* +Workflows +********* + +The following categories contain guides about different typical workflows while using the ``backpy`` CLI. +These guides' primary motivation is helping you to get used to the basic concept and optimal usage of the package. + +.. note:: + Since the package is built for reproducibility, the guides will primarily talk about using the commands of the CLI + **without** using the ``--interactive`` flag. + Read more in the section about the :ref:`interactive-mode`. + + +.. grid:: 2 + :class-row: surface + + .. grid-item-card:: :octicon:`gear` Setup + :link: workflows-setup + :link-type: ref + + Setting up the package, remotes, backup spaces and more. + + .. grid-item-card:: :octicon:`globe` Common + :link: workflows-common + :link-type: ref + + Day-to-day operations like creating, restoring and locking of backups. + +.. grid:: 2 + :class-row: surface + + .. grid-item-card:: :octicon:`cpu` Automation + :link: workflows-automation + :link-type: ref + + Automating the creation of backups to create an autonomous backup cycle. + + .. grid-item-card:: :octicon:`tools` Maintenance + :link: workflows-maintenance + :link-type: ref + + Checking for problems, cleaning up or migrating the backups. + +.. toctree:: + :includehidden: + :hidden: + + automation/index + common/index + maintenance/index + setup/index \ No newline at end of file diff --git a/docs/workflows/maintenance/cleanup.rst b/docs/workflows/maintenance/cleanup.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/workflows/maintenance/index.rst b/docs/workflows/maintenance/index.rst new file mode 100644 index 0000000..d6b24e8 --- /dev/null +++ b/docs/workflows/maintenance/index.rst @@ -0,0 +1,5 @@ +.. _workflows-maintenance: + +*********** +Maintenance +*********** diff --git a/docs/workflows/maintenance/migrating-backpy.rst b/docs/workflows/maintenance/migrating-backpy.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/workflows/maintenance/move-remotes.rst b/docs/workflows/maintenance/move-remotes.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/workflows/setup/creating-a-file-backup-space.rst b/docs/workflows/setup/creating-a-file-backup-space.rst new file mode 100644 index 0000000..0a79642 --- /dev/null +++ b/docs/workflows/setup/creating-a-file-backup-space.rst @@ -0,0 +1,5 @@ +.. _workflows-creating-a-file-backup-space: + +Creating a File Backup Space +---------------------------- + diff --git a/docs/workflows/setup/creating-a-remote.rst b/docs/workflows/setup/creating-a-remote.rst new file mode 100644 index 0000000..cddffd8 --- /dev/null +++ b/docs/workflows/setup/creating-a-remote.rst @@ -0,0 +1,99 @@ +.. _workflows-connect-to-remote: + +Creating a Remote +---------------------- + +Connecting to a remote is relatively simple. +You will need to know the following things before doing so: + +``name`` + The name you want to give the remote. This can be an arbitrary one-word name but it has to be exclusive for this remote. +``hostname`` + The hostname or IP of your remote storage (e.g. ``172.217.23.110`` or ``mystoragebox.mydomain.com``) +``username`` + The username of the account that can access the storage server. +``protocol`` + The protocol via which the files should be transferred. Possible values are given in the CLI-reference under :ref:`remote `. + + .. important:: + The remote server has to support the given protocol and all necessary ports (e.g. ``22`` for default SFTP transfers) have to be opened! + + Also note that not every protocol supports every authentication method. + +Authentication method + You have to know via which authentication method you want to log in. In general you can choose between **SSH-Key** or **password** based authentication. + We encourage you to use a public/private key combination to ensure maximum security for your remote storage. + + If you want to use + + 1. **Password**: You do not have to provide extra keywords. You will be asked for the password during the creation process. + 2. **SSH-Key**: You will have to know the location of your **private** keyfile. This requires the previous creation of such a key and the deployment of the public + key on the remote server. For further information on this refer to this page: https://wiki.archlinux.org/title/SSH_keys + +``key`` + This is the location of the keyfile as described above. + +.. note:: + + There are more options you can change depending on your system. Check the reference for :ref:`cli-remote` for further information. + +The next steps depend on your choice of authentication. Choose the right guide for you. + +.. tab-set:: + + .. tab-item:: :octicon:`key` With SSH-Key + + .. code-block:: bash + + backpy remote create --name --hostname --username --protocol --key + + After sending the command, you will be asked to enter the passphrase for the SSH key. + Since an SSH key does not require a passphrase this can be empty. + + .. code-block:: + + > Enter the passphrase for the SSH key (may be empty): █ + + This passphrase will only be saved in your local config for this remote. + The passphrase is not saved in clear text but as an encrypted token. + Read more about the encryption of ``backpy`` in the concept :ref:`concepts_encryption`. + + After entering the correct passphrase, you should see lines like these: + + .. code-block:: + + Created remote test (Hostname: , User: ). + Using Protocol sftp. + ⠼ Testing connection to with user using public key authentication. + Connection test to with user testuser was successful. + + .. tab-item:: :octicon:`passkey-fill` With Password + + .. code-block:: bash + + backpy remote create --name --hostname --username --protocol + + After sending the command, you will be asked to enter your password. + + .. code-block:: + + > Enter the password for the user: █ + + This password will only be saved in your local config for this remote. + The password is not saved in clear text but as an encrypted token. + Read more about the encryption of ``backpy`` in the concept :ref:`concepts_encryption`. + + If you entered the wrong password or passphrase you will receive an error message and the remote will not be created. + + After entering the correct passphrase, you should see lines like these: + + .. code-block:: + + Created remote test (Hostname: , User: ). + Using Protocol sftp. + ⠼ Testing connection to with user using password authentication. + Connection test to with user testuser was successful. + +If everything worked, you should have a working remote storage now. +You can use :code:`backpy remote edit ` to edit the remote, :code:`backpy remote test ` to test the connection and +:code:`backpy remote delete ` to delete the remote. diff --git a/docs/workflows/setup/index.rst b/docs/workflows/setup/index.rst new file mode 100644 index 0000000..49068bf --- /dev/null +++ b/docs/workflows/setup/index.rst @@ -0,0 +1,12 @@ +.. _workflows-setup: + +***** +Setup +***** + +These guides give an overview for some setup actions. + +.. toctree:: + creating-a-remote + creating-a-file-backup-space + diff --git a/logs.txt b/logs.txt new file mode 100644 index 0000000..e95750c --- /dev/null +++ b/logs.txt @@ -0,0 +1,14 @@ +2025-12-22T04:52:06.2270018Z ##[group]Run install -m 600 -D /dev/null ~/.ssh/id_key +2025-12-22T04:52:06.2270463Z [36;1minstall -m 600 -D /dev/null ~/.ssh/id_key[0m +2025-12-22T04:52:06.2270787Z [36;1mecho "$SSH_PRIVATE_KEY" > ~/.ssh/id_key[0m +2025-12-22T04:52:06.2271451Z [36;1mssh-keyscan -vvv -H "$SSH_HOSTNAME" > ~/.ssh/known_hosts[0m +2025-12-22T04:52:06.2304197Z shell: /usr/bin/bash -e {0} +2025-12-22T04:52:06.2304447Z env: +2025-12-22T04:52:06.2304716Z pythonLocation: /opt/hostedtoolcache/Python/3.14.2/x64 +2025-12-22T04:52:06.2305147Z PKG_CONFIG_PATH: /opt/hostedtoolcache/Python/3.14.2/x64/lib/pkgconfig +2025-12-22T04:52:06.2305574Z Python_ROOT_DIR: /opt/hostedtoolcache/Python/3.14.2/x64 +2025-12-22T04:52:06.2305955Z Python2_ROOT_DIR: /opt/hostedtoolcache/Python/3.14.2/x64 +2025-12-22T04:52:06.2306324Z Python3_ROOT_DIR: /opt/hostedtoolcache/Python/3.14.2/x64 +2025-12-22T04:52:06.2306847Z LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.14.2/x64/lib +2025-12-22T04:52:06.2307159Z ##[endgroup] +2025-12-22T04:52:06.2487878Z ##[error]Process completed with exit code 1. diff --git a/pyproject.toml b/pyproject.toml index cfcb1db..f58c09b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,9 +30,8 @@ classifiers = [ requires-python = ">=3.12" dependencies = [ "numpy", - "toml", + "tomli-w", "rich", - "pandas", "click", "rich-click", "click-params", @@ -45,7 +44,13 @@ dependencies = [ "catppuccin", ] -[project.optional-dependencies] +[dependency-groups] + +all = [ + {include-group = "dev"}, + {include-group = "docs"}, + {include-group = "tests"}, +] dev = [ "jupyterlab", @@ -53,13 +58,24 @@ dev = [ ] docs = [ - "sphinx", - "sphinx-autobuild", + "sphinx", + "sphinx-autobuild", + "sphinx-iconify", + "sphinx-design", + "sphinx-copybutton", + "sphinx-togglebutton", + "sphinx-click", + "sphinx-automodapi", + "numpydoc", + "shibuya", + "towncrier", + {include-group = "dev"}, ] tests = [ "pytest >= 7.0", "pytest-cov", + {include-group = "dev"} ] [project.scripts] @@ -74,3 +90,26 @@ write_to = "backpy/_version.py" [tool.setuptools.packages.find] where = ["."] + +[tool.towncrier] +directory = "docs/changes" +filename = "docs/changes/CHANGELOG.rst" +package = "backpy" +issue_format = "`#{issue} `_" +ignore = ["tutorial-towncrier.rst", "CHANGELOG.rst"] + +[[tool.towncrier.type]] +directory = "api" +name = "API Changes" + +[[tool.towncrier.type]] +directory = "cli" +name = "CLI Changes" + +[[tool.towncrier.type]] +directory = "docs" +name = "Documentation Changes" + +[[tool.towncrier.type]] +directory = "bugfix" +name = "Bug Fixes"