├── .coveragerc ├── .flake8 ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ └── bug.yml ├── dependabot.yml ├── labeler.yml ├── labels.yml └── workflows │ ├── autoupdate_python_versions.yml │ ├── ci_cd.yml │ └── label.yml ├── .gitignore ├── .pre-commit-config.yaml ├── AUTHORS.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.rst ├── SECURITY.md ├── doc ├── .vale.ini ├── Makefile ├── make.bat ├── source │ ├── _static │ │ ├── README.md │ │ ├── ansys_python_manager.PNG │ │ ├── create_venv_tab.PNG │ │ ├── general_pkg_management.PNG │ │ ├── launch_options.PNG │ │ ├── manage_venv_tab.PNG │ │ ├── pyansys_documentation.PNG │ │ └── pyansys_pkg_management.PNG │ ├── _templates │ │ └── README.md │ ├── conf.py │ ├── contributing.rst │ ├── index.rst │ ├── installer.rst │ └── user_guide.rst └── styles │ ├── .gitignore │ └── config │ └── vocabularies │ └── ANSYS │ ├── accept.txt │ └── reject.txt ├── examples └── README.md ├── frozen.spec ├── images └── app-image.png ├── linux ├── debian │ ├── ansys_python_manager_prebuid.desktop │ ├── fpm-options-debian │ ├── installer.sh │ └── postInstallScript.sh └── non-debian │ ├── ansys_python_manager.desktop │ ├── fpm-options-centos │ ├── fpm-options-fedora │ ├── installer_CentOS.sh │ └── updater_CentOS.sh ├── make.bat ├── pyproject.toml ├── scripts ├── requirements.txt └── update_python_versions.py ├── setup.nsi ├── src └── ansys │ └── tools │ └── installer │ ├── VERSION │ ├── __init__.py │ ├── __main__.py │ ├── assets │ ├── ansys-favicon.ico │ ├── ansys-favicon.png │ ├── pyansys-dark-crop.png │ ├── pyansys-light-crop.png │ ├── pyansys-light.png │ ├── pyansys_icon.ico │ ├── pyansys_icon.svg │ └── scripts │ │ ├── uninstaller_ubuntu.sh │ │ └── uninstaller_yum.sh │ ├── auto_updater.py │ ├── common.py │ ├── configure.py │ ├── configure_json.py │ ├── constants.py │ ├── create_virtual_environment.py │ ├── find_python.py │ ├── installed_table.py │ ├── installer.py │ ├── linux_functions.py │ ├── main.py │ ├── misc.py │ ├── progress_bar.py │ ├── uninstall.py │ ├── vscode.py │ └── windows_functions.py ├── tests ├── test_auto_updater.py ├── test_gui.py ├── test_linux_functions.py └── test_metadata.py └── uninstall.nsi /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = 3 | ansys.tools 4 | 5 | [report] 6 | show_missing = true 7 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = venv, __init__.py, build, doc/source/examples 3 | # To be added after refactoring code to be compliant: E501 4 | select = W191, W291, W293, W391, E115, E117, E122, E124, E125, E225, E231, E301, E303, F401, F403 5 | count = True 6 | max-complexity = 10 7 | max-line-length = 88 8 | statistics = True 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto !eol 2 | *.sh eol=lf 3 | *.bat eol=crlf 4 | *.zip filter=lfs diff=lfs merge=lfs -text 5 | *.gz filter=lfs diff=lfs merge=lfs -text 6 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence, 6 | # they will be requested for review when someone opens a pull request. 7 | * @RobPasMue 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug, problem, error 2 | description: Issue reporting template 3 | title: "[BUG] Problem found in..." 4 | labels: ["bug"] 5 | assignees: [""] 6 | 7 | body: 8 | 9 | - type: checkboxes 10 | id: opt-out-checklist 11 | attributes: 12 | label: '🔍 Before submitting the issue' 13 | description: Please, make sure the following conditions are met 14 | options: 15 | - label: I have tried the latest Ansys Python Installer version 16 | required: true 17 | - label: I have adapted properly the title of the issue 18 | required: true 19 | 20 | - type: textarea 21 | id: information 22 | attributes: 23 | label: 'Description' 24 | placeholder: Please add the required information to reproduce your problem (logs, screenshots, description...) 25 | validations: 26 | required: true 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | labels: 8 | - "maintenance" 9 | - "dependencies" 10 | 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: "weekly" 15 | labels: 16 | - "maintenance" 17 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | documentation: 2 | - changed-files: 3 | - any-glob-to-any-file: ['doc/source/**/*'] 4 | maintenance: 5 | - changed-files: 6 | - any-glob-to-any-file: ['.github/**/*', '.flake8', 'pyproject.toml'] 7 | dependencies: 8 | - changed-files: 9 | - any-glob-to-any-file: ['requirements/*'] 10 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | - name: bug 2 | description: Something isn't working 3 | color: d42a34 4 | 5 | - name: dependencies 6 | description: Related with project dependencies 7 | color: ffc0cb 8 | 9 | - name: documentation 10 | description: Improvements or additions to documentation 11 | color: 0677ba 12 | 13 | - name: enhancement 14 | description: New features or code improvements 15 | color: FFD827 16 | 17 | - name: good first issue 18 | description: Easy to solve for newcomers 19 | color: 62ca50 20 | 21 | - name: maintenance 22 | description: Package and maintenance related 23 | color: f78c37 24 | 25 | - name: release 26 | description: Anything related to an incoming release 27 | color: ffffff 28 | -------------------------------------------------------------------------------- /.github/workflows/autoupdate_python_versions.yml: -------------------------------------------------------------------------------- 1 | name: Autoupdate Python Versions 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | # Or if the "scripts" directory is modified 7 | push: 8 | paths: 9 | - 'scripts/**' 10 | - '.github/workflows/autoupdate_python_versions.yml' 11 | 12 | env: 13 | MAIN_PYTHON_VERSION: '3.12' 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | autoupdate-python-versions: 21 | name: Autoupdate Python Versions 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | with: 27 | token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} 28 | 29 | - name: Install the latest version of uv and set the python version 30 | uses: astral-sh/setup-uv@v6 31 | with: 32 | python-version: ${{ env.MAIN_PYTHON_VERSION }} 33 | cache-dependency-glob: '**/pyproject.toml' 34 | activate-environment: true 35 | 36 | - name: Run the autoupdate script 37 | run: | 38 | uv pip install -r scripts/requirements.txt 39 | uv run scripts/update_python_versions.py 40 | 41 | - name: Commit changes (if any) 42 | uses: peter-evans/create-pull-request@v7 43 | with: 44 | commit-message: 'chore: update Python versions' 45 | token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} 46 | title: 'chore: update Python versions' 47 | body: 'This PR updates the Python versions in the CI configuration files.' 48 | branch: feat/update-python-version 49 | base: main 50 | delete-branch: true 51 | add-paths: src/ansys/tools/installer/constants.py 52 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | name: Labeler 2 | on: 3 | pull_request: 4 | push: 5 | branches: [ main ] 6 | paths: 7 | - '../labels.yml' 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | 15 | label-syncer: 16 | name: Syncer 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: micnncim/action-label-syncer@v1 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | 24 | labeler: 25 | name: Set labels 26 | needs: [label-syncer] 27 | permissions: 28 | contents: read 29 | pull-requests: write 30 | runs-on: ubuntu-latest 31 | steps: 32 | 33 | # Label based on modified files 34 | - name: Label based on changed files 35 | uses: actions/labeler@v5 36 | with: 37 | repo-token: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | # Label based on branch name 40 | - uses: actions-ecosystem/action-add-labels@v1 41 | if: | 42 | startsWith(github.event.pull_request.head.ref, 'doc') || 43 | startsWith(github.event.pull_request.head.ref, 'docs') 44 | with: 45 | labels: documentation 46 | 47 | - uses: actions-ecosystem/action-add-labels@v1 48 | if: | 49 | startsWith(github.event.pull_request.head.ref, 'maint') || 50 | startsWith(github.event.pull_request.head.ref, 'no-ci') || 51 | startsWith(github.event.pull_request.head.ref, 'ci') 52 | with: 53 | labels: maintenance 54 | 55 | - uses: actions-ecosystem/action-add-labels@v1 56 | if: startsWith(github.event.pull_request.head.ref, 'feat') 57 | with: 58 | labels: | 59 | enhancement 60 | 61 | - uses: actions-ecosystem/action-add-labels@v1 62 | if: | 63 | startsWith(github.event.pull_request.head.ref, 'fix') || 64 | startsWith(github.event.pull_request.head.ref, 'patch') 65 | with: 66 | labels: bug 67 | 68 | commenter: 69 | runs-on: ubuntu-latest 70 | steps: 71 | - name: Suggest to add labels 72 | uses: peter-evans/create-or-update-comment@v4 73 | # Execute only when no labels have been applied to the pull request 74 | if: toJSON(github.event.pull_request.labels.*.name) == '{}' 75 | with: 76 | issue-number: ${{ github.event.pull_request.number }} 77 | body: | 78 | Please add one of the following labels to add this contribution to the Release Notes :point_down: 79 | - [bug](https://github.com/ansys/pyproduct-library/pulls?q=label%3Abug+) 80 | - [documentation](https://github.com/ansys/pyproduct-library/pulls?q=label%3Adocumentation+) 81 | - [enhancement](https://github.com/ansys/pyproduct-library/pulls?q=label%3Aenhancement+) 82 | - [good first issue](https://github.com/ansys/pyproduct-library/pulls?q=label%3Agood+first+issue) 83 | - [maintenance](https://github.com/ansys/pyproduct-library/pulls?q=label%3Amaintenance+) 84 | - [release](https://github.com/ansys/pyproduct-library/pulls?q=label%3Arelease+) 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # emacs 14 | *~ 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | share/python-wheels/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | MANIFEST 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .nox/ 50 | .cov 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | *.py,cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | cover/ 61 | package*/ 62 | *.deb 63 | # Translations 64 | *.mo 65 | *.pot 66 | 67 | # Django stuff: 68 | *.log 69 | local_settings.py 70 | db.sqlite3 71 | db.sqlite3-journal 72 | 73 | # Flask stuff: 74 | instance/ 75 | .webassets-cache 76 | 77 | # Scrapy stuff: 78 | .scrapy 79 | 80 | # Sphinx documentation 81 | doc/_build/ 82 | doc/source/images 83 | 84 | # PyBuilder 85 | .pybuilder/ 86 | target/ 87 | 88 | # Jupyter Notebook 89 | .ipynb_checkpoints 90 | 91 | # IPython 92 | profile_default/ 93 | ipython_config.py 94 | 95 | # pyenv 96 | # For a library or package, you might want to ignore these files since the code is 97 | # intended to run in multiple environments; otherwise, check them in: 98 | # .python-version 99 | 100 | # pipenv 101 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 102 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 103 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 104 | # install all needed dependencies. 105 | #Pipfile.lock 106 | 107 | # poetry 108 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 109 | # This is especially recommended for binary packages to ensure reproducibility, and is more 110 | # commonly ignored for libraries. 111 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 112 | #poetry.lock 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv* 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | 164 | # End of https://www.toptal.com/developers/gitignore/api/python 165 | .vscode/settings.json 166 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Integration with GitHub Actions 2 | # See https://pre-commit.ci/ 3 | ci: 4 | autofix_prs: true 5 | autoupdate_schedule: weekly 6 | 7 | repos: 8 | - repo: https://github.com/pycqa/isort 9 | rev: 6.0.1 10 | hooks: 11 | - id: isort 12 | args: 13 | - --line-length=88 14 | 15 | - repo: https://github.com/psf/black 16 | rev: 25.1.0 17 | hooks: 18 | - id: black 19 | args: 20 | - --line-length=88 21 | 22 | - repo: https://github.com/keewis/blackdoc 23 | rev: v0.3.9 24 | hooks: 25 | - id: blackdoc 26 | files: '\.py$' 27 | 28 | - repo: https://github.com/PyCQA/flake8 29 | rev: 7.2.0 30 | hooks: 31 | - id: flake8 32 | 33 | - repo: https://github.com/codespell-project/codespell 34 | rev: v2.4.1 35 | hooks: 36 | - id: codespell 37 | args: ["--toml", "pyproject.toml"] 38 | additional_dependencies: ["tomli"] 39 | 40 | - repo: https://github.com/pycqa/pydocstyle 41 | rev: 6.3.0 42 | hooks: 43 | - id: pydocstyle 44 | additional_dependencies: [tomli] 45 | exclude: "tests/" 46 | 47 | - repo: https://github.com/pre-commit/pre-commit-hooks 48 | rev: v5.0.0 49 | hooks: 50 | - id: check-merge-conflict 51 | - id: debug-statements 52 | - id: check-yaml 53 | - id: trailing-whitespace 54 | 55 | # this validates our github workflow files 56 | - repo: https://github.com/python-jsonschema/check-jsonschema 57 | rev: 0.33.0 58 | hooks: 59 | - id: check-github-workflows 60 | 61 | - repo: https://github.com/ansys/pre-commit-hooks 62 | rev: v0.5.2 63 | hooks: 64 | - id: add-license-headers 65 | args: 66 | - --start_year=2023 67 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Authors 2 | 3 | ## Project Lead 4 | 5 | * [Roberto Pastor Muela](https://github.com/RobPasMue) 6 | 7 | ## Contributors 8 | 9 | * [Alexander Kaszinsky](https://github.com/akaszynski) 10 | * [Ramsobhan Kokkiligadda](https://github.com/rs-bh-n) 11 | * [Abhishek Chitwar](https://github.com/abhishekchitwar) 12 | * [Sandeep Medikonda](https://github.com/smedikon) 13 | * [Rajesh Meena](https://github.com/rajesh1359) 14 | * [kmahajan-cadfem](https://github.com/kmahajan-cadfem) 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our 7 | project and our community a harassment-free experience for everyone, 8 | regardless of age, body size, disability, ethnicity, sex 9 | characteristics, gender identity and expression, level of experience, 10 | education, socioeconomic status, nationality, personal appearance, 11 | race, religion, or sexual identity and orientation. 12 | 13 | ## Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | * Using welcoming and inclusive language 19 | * Being respectful of differing viewpoints and experiences 20 | * Gracefully accepting constructive criticism 21 | * Focusing on what is best for the community 22 | * Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | * The use of sexualized language or imagery and unwelcome sexual 27 | attention or advances 28 | * Trolling, insulting/derogatory comments, and personal or political attacks 29 | * Public or private harassment 30 | * Publishing others' private information, such as a physical or electronic 31 | address, without explicit permission 32 | * Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | ## Our Responsibilities 36 | 37 | Project maintainers are responsible for clarifying the standards of acceptable 38 | behavior and are expected to take appropriate and fair corrective action in 39 | response to any instances of unacceptable behavior. 40 | 41 | Project maintainers have the right and responsibility to remove, edit, or reject 42 | comments, commits, code, wiki edits, issues, and other contributions that are 43 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 44 | contributor for other behaviors that they deem inappropriate, threatening, 45 | offensive, or harmful. 46 | 47 | ## Scope 48 | 49 | This Code of Conduct applies both within project spaces and in public spaces 50 | when an individual is representing the project or its community. Examples of 51 | representing a project or community include using an official project e-mail 52 | address, posting via an official social media account, or acting as an appointed 53 | representative at an online or offline event. Representation of a project may be 54 | further defined and clarified by project maintainers. 55 | 56 | ## Attribution 57 | 58 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 59 | version 1.4, available at 60 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 61 | 62 | [homepage]: https://www.contributor-covenant.org 63 | 64 | For answers to common questions about this code of conduct, see 65 | https://www.contributor-covenant.org/faq 66 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Any code contributions are welcome and it is the developer team's hope that this 4 | guide facilitates an understanding of the ``ansys-tools-installer`` code 5 | repository. It is important to note that while the ``ansys-tools-installer`` software 6 | package is maintained by ANSYS and any submissions are reviewed 7 | thoroughly before merging, we still seek to foster a community that can 8 | support user questions and develop new features to make this software 9 | a useful tool for all users. As such, we welcome and encourage any 10 | questions or submissions to this repository. 11 | 12 | For contributing to this project, please refer to the [PyAnsys Developer's Guide]. 13 | Further information about contributing to ``ansys-tools-installer`` can be found in [Contributing]. 14 | 15 | [PyAnsys Developer's Guide]: https://dev.docs.pyansys.com/index.html 16 | [Contributing]: https://installer.tools.docs.pyansys.com/version/dev/contributing.html 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Simple makefile to simplify repetitive build env management tasks under posix 2 | .PHONY: setup install tests doc build clean fresh-build 3 | 4 | setup: 5 | @echo "Installing uv..." 6 | python -m pip install -U pip uv 7 | 8 | install: setup 9 | @echo "Installing..." 10 | uv pip install -e .[freeze] 11 | @echo "Installation complete." 12 | 13 | tests: setup 14 | @echo "Installing test dependencies..." 15 | uv pip install -e .[tests] 16 | @echo "Running tests..." 17 | uv run pytest 18 | @echo "Tests complete." 19 | 20 | doc: setup 21 | @echo "Installing documentation dependencies..." 22 | uv pip install -e .[doc] 23 | @echo "Building documentation..." 24 | cd doc && make clean && make html && cd .. 25 | @echo "Documentation complete." 26 | 27 | build: setup 28 | @echo "Freezing using pyinstaller" 29 | uv run pyinstaller frozen.spec 30 | 31 | clean: 32 | @echo "Cleaning up build files..." 33 | rm -rf build dist 34 | 35 | fresh-build: clean build 36 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Ansys Python Manager (QT) 2 | ========================= 3 | 4 | |pyansys| |GH-CI| |MIT| |black| |pre-commit-ci| 5 | 6 | .. |pyansys| image:: https://img.shields.io/badge/Py-Ansys-ffc107.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABDklEQVQ4jWNgoDfg5mD8vE7q/3bpVyskbW0sMRUwofHD7Dh5OBkZGBgW7/3W2tZpa2tLQEOyOzeEsfumlK2tbVpaGj4N6jIs1lpsDAwMJ278sveMY2BgCA0NFRISwqkhyQ1q/Nyd3zg4OBgYGNjZ2ePi4rB5loGBhZnhxTLJ/9ulv26Q4uVk1NXV/f///////69du4Zdg78lx//t0v+3S88rFISInD59GqIH2esIJ8G9O2/XVwhjzpw5EAam1xkkBJn/bJX+v1365hxxuCAfH9+3b9/+////48cPuNehNsS7cDEzMTAwMMzb+Q2u4dOnT2vWrMHu9ZtzxP9vl/69RVpCkBlZ3N7enoDXBwEAAA+YYitOilMVAAAAAElFTkSuQmCC 7 | :target: https://docs.pyansys.com/ 8 | :alt: PyAnsys 9 | 10 | .. |GH-CI| image:: https://github.com/ansys/python-installer-qt-gui/actions/workflows/ci_cd.yml/badge.svg 11 | :target: https://github.com/ansys/python-installer-qt-gui/actions/workflows/ci_cd.yml 12 | :alt: GH-CI 13 | 14 | .. |MIT| image:: https://img.shields.io/badge/License-MIT-yellow.svg 15 | :target: https://opensource.org/licenses/MIT 16 | :alt: MIT 17 | 18 | .. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=flat 19 | :target: https://github.com/psf/black 20 | :alt: Black 21 | 22 | .. |pre-commit-ci| image:: https://results.pre-commit.ci/badge/github/ansys/python-installer-qt-gui/main.svg 23 | :target: https://results.pre-commit.ci/latest/github/ansys/python-installer-qt-gui/main 24 | :alt: pre-commit.ci status 25 | 26 | .. readme_start 27 | 28 | This is a simple cross-platform `QT `_ application 29 | you can use to install Python and (optional) PyAnsys packages. 30 | 31 | .. image:: images/app-image.png 32 | :align: center 33 | :alt: Screenshot of Python Installer application 34 | 35 | .. contribute_start 36 | 37 | Installation 38 | ~~~~~~~~~~~~ 39 | Visit the `Releases 40 | `__ page and pull 41 | down the latest installer. This is a simple application you can use to install 42 | Python and manage your Python environment. 43 | 44 | 45 | For developers 46 | ^^^^^^^^^^^^^^ 47 | You can be up and running with four lines of code: 48 | 49 | .. code:: bash 50 | 51 | git clone https://github.com/ansys/python-installer-qt-gui 52 | cd python-installer-qt-gui 53 | python -m pip install -U pip uv 54 | uv venv 55 | uv pip install -e . 56 | 57 | Now you can run it with: 58 | 59 | .. code:: bash 60 | 61 | uv run ansys_python_installer 62 | 63 | **Details** 64 | 65 | Installing Pytools installer in developer mode allows you to modify the source 66 | and enhance it. 67 | 68 | Before contributing to the project, please refer to the `PyAnsys Developer's 69 | guide`_. You will need to follow these steps: 70 | 71 | #. Start by cloning this repository: 72 | 73 | .. code:: bash 74 | 75 | git clone https://github.com/ansys/python-installer-qt-gui 76 | 77 | #. Create a fresh-clean Python environment and activate it. Refer to the 78 | official `venv`_ documentation if you require further information: 79 | 80 | .. code:: bash 81 | 82 | # Create a virtual environment 83 | python -m uv venv .venv 84 | 85 | # Activate it in a POSIX system 86 | source .venv/bin/activate 87 | 88 | # Activate it in Windows CMD environment 89 | .venv\Scripts\activate.bat 90 | 91 | # Activate it in Windows Powershell 92 | .venv\Scripts\Activate.ps1 93 | 94 | #. Make sure you have the latest version of `pip`_: 95 | 96 | .. code:: bash 97 | 98 | python -m pip install -U pip uv 99 | 100 | #. Install the project in editable mode: 101 | 102 | .. code:: bash 103 | 104 | python -m uv pip install -e .[tests,doc] 105 | 106 | #. Finally, verify your development installation by running: 107 | 108 | .. code:: bash 109 | 110 | uv run pytest tests -v 111 | 112 | 113 | Style and testing 114 | ----------------- 115 | This project uses `pre-commit `_. Install with: 116 | 117 | .. code:: 118 | 119 | uv pip install pre-commit 120 | uv run pre-commit install 121 | 122 | This will now run ``pre-commit`` for each commit to ensure you follow project 123 | style guidelines. For example: 124 | 125 | .. code:: 126 | 127 | git commit -am 'fix style' 128 | isort....................................................................Passed 129 | black....................................................................Passed 130 | blacken-docs.............................................................Passed 131 | flake8...................................................................Passed 132 | codespell................................................................Passed 133 | pydocstyle...............................................................Passed 134 | check for merge conflicts................................................Passed 135 | debug statements (python)................................................Passed 136 | check yaml...............................................................Passed 137 | trim trailing whitespace.................................................Passed 138 | Validate GitHub Workflows................................................Passed 139 | 140 | If you need to run it again on all files and not just staged files, run: 141 | 142 | .. code:: 143 | 144 | uv run pre-commit run --all-files 145 | 146 | 147 | Local build 148 | ----------- 149 | This application can be deployed as a 'frozen' application using `pyinstaller 150 | `_ with: 151 | 152 | .. code:: 153 | 154 | uv pip install -e .[freeze] 155 | uv run pyinstaller frozen.spec 156 | 157 | This will generate application files at ``dist/ansys_python_manager`` and you 158 | can run it locally by executing ``Ansys Python Manager.exe``. 159 | 160 | 161 | Documentation 162 | ------------- 163 | For building documentation, you can either run the usual rules provided in the 164 | `Sphinx`_ Makefile, such us: 165 | 166 | .. code:: bash 167 | 168 | uv pip install -e .[doc] 169 | uv run make -C doc/ html 170 | 171 | # subsequently open the documentation with (under Linux): 172 | doc/html/index.html 173 | 174 | 175 | Distributing 176 | ------------ 177 | This project is vectored to be an open-source project. For the time being, feel 178 | free to distribute it internally, but direct users to visit the `Releases 179 | `__ page 180 | 181 | Security 182 | -------- 183 | The versions that are still supported for security updates can be found at 184 | the `Security guidelines `_ 185 | site. Information on how to report vulenrabilities is also found. 186 | 187 | 188 | .. LINKS AND REFERENCES 189 | .. _black: https://github.com/psf/black 190 | .. _flake8: https://flake8.pycqa.org/en/latest/ 191 | .. _isort: https://github.com/PyCQA/isort 192 | .. _PyAnsys Developer's guide: https://dev.docs.pyansys.com/ 193 | .. _pre-commit: https://pre-commit.com/ 194 | .. _pytest: https://docs.pytest.org/en/stable/ 195 | .. _Sphinx: https://www.sphinx-doc.org/en/master/ 196 | .. _pip: https://pypi.org/project/pip/ 197 | .. _tox: https://tox.wiki/ 198 | .. _venv: https://docs.python.org/3/library/venv.html 199 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | At the moment, all Ansys Python Manager versions are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | dev | :white_check_mark: | 11 | | 0.3.x | :white_check_mark: | 12 | | 0.2.x | :white_check_mark: | 13 | | 0.1.x | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | If you detect a vulnerability, please refer to the 18 | [Issues](https://github.com/ansys/python-installer-qt-gui/issues) 19 | section of this repository and open an issue. 20 | -------------------------------------------------------------------------------- /doc/.vale.ini: -------------------------------------------------------------------------------- 1 | # Core settings 2 | # ============= 3 | 4 | # Location of our `styles` 5 | StylesPath = "styles" 6 | 7 | # The options are `suggestion`, `warning`, or `error` (defaults to “warning”). 8 | MinAlertLevel = warning 9 | 10 | # By default, `code` and `tt` are ignored. 11 | IgnoredScopes = code, tt 12 | 13 | # By default, `script`, `style`, `pre`, and `figure` are ignored. 14 | SkippedScopes = script, style, pre, figure 15 | 16 | # WordTemplate specifies what Vale will consider to be an individual word. 17 | WordTemplate = \b(?:%s)\b 18 | 19 | # List of Packages to be used for our guidelines 20 | Packages = Google 21 | 22 | # Define the Ansys vocabulary 23 | Vocab = ANSYS 24 | 25 | [*.{md,rst}] 26 | 27 | # Apply the following styles 28 | BasedOnStyles = Vale, Google 29 | Vale.Terms = NO 30 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = -j auto -W --color 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = _build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 20 | 21 | 22 | # Customized clean due to examples gallery 23 | clean: 24 | rm -rf $(BUILDDIR)/* 25 | rm -rf $(SOURCEDIR)/examples 26 | rm -rf $(SOURCEDIR)/images 27 | find . -type d -name "_autosummary" -exec rm -rf {} + 28 | 29 | # Create PDF 30 | pdf: 31 | @$(SPHINXBUILD) -M latex "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 32 | cd $(BUILDDIR)/latex && latexmk -r latexmkrc -pdf *.tex -interaction=nonstopmode || true 33 | (test -f $(BUILDDIR)/latex/*.pdf && echo pdf exists) || exit 1 34 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | if "%1" == "clean" goto clean 15 | if "%1" == "pdf" goto pdf 16 | 17 | %SPHINXBUILD% >NUL 2>NUL 18 | if errorlevel 9009 ( 19 | echo. 20 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 21 | echo.installed, then set the SPHINXBUILD environment variable to point 22 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 23 | echo.may add the Sphinx directory to PATH. 24 | echo. 25 | echo.If you don't have Sphinx installed, grab it from 26 | echo.http://sphinx-doc.org/ 27 | exit /b 1 28 | ) 29 | 30 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 31 | goto end 32 | 33 | :clean 34 | rmdir /s /q %BUILDDIR% > /NUL 2>&1 35 | rmdir /s /q %SOURCEDIR%\examples > /NUL 2>&1 36 | rmdir /s /q %SOURCEDIR%\images > /NUL 2>&1 37 | for /d /r %SOURCEDIR% %%d in (_autosummary) do @if exist "%%d" rmdir /s /q "%%d" 38 | goto end 39 | 40 | :help 41 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 42 | 43 | :pdf 44 | %SPHINXBUILD% -M latex %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 45 | cd "%BUILDDIR%\latex" 46 | for %%f in (*.tex) do ( 47 | pdflatex "%%f" --interaction=nonstopmode) 48 | if NOT EXIST python-installer-qt-gui.pdf ( 49 | Echo "no pdf generated!" 50 | exit /b 1) 51 | Echo "pdf generated!" 52 | 53 | :end 54 | popd 55 | -------------------------------------------------------------------------------- /doc/source/_static/README.md: -------------------------------------------------------------------------------- 1 | Static files are found here (like images and other assets). 2 | -------------------------------------------------------------------------------- /doc/source/_static/ansys_python_manager.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/doc/source/_static/ansys_python_manager.PNG -------------------------------------------------------------------------------- /doc/source/_static/create_venv_tab.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/doc/source/_static/create_venv_tab.PNG -------------------------------------------------------------------------------- /doc/source/_static/general_pkg_management.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/doc/source/_static/general_pkg_management.PNG -------------------------------------------------------------------------------- /doc/source/_static/launch_options.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/doc/source/_static/launch_options.PNG -------------------------------------------------------------------------------- /doc/source/_static/manage_venv_tab.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/doc/source/_static/manage_venv_tab.PNG -------------------------------------------------------------------------------- /doc/source/_static/pyansys_documentation.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/doc/source/_static/pyansys_documentation.PNG -------------------------------------------------------------------------------- /doc/source/_static/pyansys_pkg_management.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/doc/source/_static/pyansys_pkg_management.PNG -------------------------------------------------------------------------------- /doc/source/_templates/README.md: -------------------------------------------------------------------------------- 1 | ## Contains templates for the documentation build 2 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | """Sphinx documentation configuration file.""" 2 | 3 | from datetime import datetime 4 | import os 5 | import shutil 6 | 7 | from ansys_sphinx_theme import get_version_match 8 | 9 | from ansys.tools.installer import __version__ 10 | 11 | # Project information 12 | project = "python-installer-qt-gui" 13 | copyright = f"(c) {datetime.now().year} ANSYS, Inc. All rights reserved" 14 | author = "ANSYS, Inc." 15 | release = version = __version__ 16 | cname = os.getenv("DOCUMENTATION_CNAME", default="nocname.com") 17 | switcher_version = get_version_match(__version__) 18 | 19 | # Select desired theme, and declare the html title 20 | html_theme = "ansys_sphinx_theme" 21 | html_short_title = html_title = "Ansys Python Manager" 22 | 23 | # specify the location of your github repo 24 | html_context = { 25 | "github_user": "ansys", 26 | "github_repo": "python-installer-qt-gui", 27 | "github_version": "main", 28 | "doc_path": "doc/source", 29 | } 30 | html_theme_options = { 31 | "logo": "pyansys", 32 | "switcher": { 33 | "json_url": f"https://{cname}/versions.json", 34 | "version_match": switcher_version, 35 | }, 36 | "check_switcher": False, 37 | "github_url": "https://github.com/ansys/python-installer-qt-gui", 38 | "show_prev_next": False, 39 | "show_breadcrumbs": True, 40 | "additional_breadcrumbs": [ 41 | ("PyAnsys", "https://docs.pyansys.com/"), 42 | ], 43 | } 44 | 45 | # Sphinx extensions 46 | extensions = [ 47 | "sphinx.ext.autodoc", 48 | "sphinx.ext.autosummary", 49 | "sphinx.ext.intersphinx", 50 | "sphinx_copybutton", 51 | "sphinx_design", 52 | "sphinx_toolbox.collapse", 53 | ] 54 | 55 | # Intersphinx mapping 56 | intersphinx_mapping = { 57 | "python": ("https://docs.python.org/dev", None), 58 | # kept here as an example 59 | # "scipy": ("https://docs.scipy.org/doc/scipy/reference", None), 60 | # "numpy": ("https://numpy.org/devdocs", None), 61 | # "matplotlib": ("https://matplotlib.org/stable", None), 62 | # "pandas": ("https://pandas.pydata.org/pandas-docs/stable", None), 63 | # "pyvista": ("https://docs.pyvista.org/", None), 64 | # "grpc": ("https://grpc.github.io/grpc/python/", None), 65 | } 66 | 67 | # static path 68 | html_static_path = ["_static", "images"] 69 | 70 | # Add any paths that contain templates here, relative to this directory. 71 | templates_path = ["_templates"] 72 | 73 | # The suffix(es) of source filenames. 74 | source_suffix = ".rst" 75 | 76 | # The master toctree document. 77 | master_doc = "index" 78 | 79 | # Copying README images 80 | shutil.copytree("../../images", "images", dirs_exist_ok=True) 81 | -------------------------------------------------------------------------------- /doc/source/contributing.rst: -------------------------------------------------------------------------------- 1 | .. _ref_contributing: 2 | 3 | Contributing 4 | ============ 5 | 6 | Any code contributions are welcome and it is the developer team's hope that this 7 | guide facilitates an understanding of the ``ansys-tools-installer`` code 8 | repository. It is important to note that while the ``ansys-tools-installer`` software 9 | package is maintained by ANSYS and any submissions are reviewed 10 | thoroughly before merging, we still seek to foster a community that can 11 | support user questions and develop new features to make this software 12 | a useful tool for all users. As such, we welcome and encourage any 13 | questions or submissions to this repository. 14 | 15 | For contributing to this project, please refer to the 16 | `PyAnsys Developer's Guide`_. 17 | 18 | .. include:: ../../README.rst 19 | :start-after: .. contribute_start -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. 2 | Just reuse the root readme to avoid duplicating the documentation. 3 | Provide any documentation specific to your online documentation 4 | here. 5 | 6 | .. include:: ../../README.rst 7 | :start-after: .. contribute_start 8 | 9 | .. toctree:: 10 | :hidden: 11 | :maxdepth: 1 12 | 13 | installer 14 | contributing 15 | user_guide -------------------------------------------------------------------------------- /doc/source/installer.rst: -------------------------------------------------------------------------------- 1 | Usage instructions 2 | ################## 3 | 4 | Installing the ``Ansys Python Manager`` 5 | ======================================= 6 | 7 | .. tab-set:: 8 | 9 | .. tab-item:: Windows 10 | 11 | First step is installing the ``Ansys Python Manager``. In order to do so, follow the next steps. 12 | 13 | #. Download the necessary installer from the `latest available release `_. 14 | The file should be named ``Ansys-Python-Manager-Setup-v*.exe``. 15 | 16 | #. Execute the installer. 17 | 18 | #. Search for the ``Ansys Python Manager`` and run it. 19 | 20 | The ``Ansys Python Manager`` window should appear at this stage. 21 | 22 | .. tab-item:: Linux 23 | 24 | .. tab-set:: 25 | 26 | .. tab-item:: Ubuntu 27 | 28 | Prerequisites: 29 | 30 | #. **OS** supported for **Ubuntu(20.04 and 22.04)**. 31 | 32 | #. Update ``apt-get`` repository and install the following packages with **sudo** privileges: 33 | **wget, gnome, libffi-dev, libssl-dev, libsqlite3-dev, libxcb-xinerama0 and build-essential** packages with **sudo** privileges 34 | 35 | .. code:: shell 36 | 37 | sudo apt-get update -y 38 | sudo apt-get install wget gnome libffi-dev libssl-dev libsqlite3-dev libxcb-xinerama0 build-essential -y 39 | 40 | #. Install **zlib** package 41 | 42 | .. code:: shell 43 | 44 | wget https://zlib.net/current/zlib.tar.gz 45 | tar xvzf zlib.tar.gz 46 | cd zlib-* 47 | make clean 48 | ./configure 49 | make 50 | sudo make install 51 | 52 | To install the ``Ansys Python Manager``, follow below steps. 53 | 54 | #. Download the necessary installer from the `latest available release `_. 55 | The file should be named ``Ansys-Python-Manager_*.zip``. 56 | 57 | #. Execute the below command on the terminal 58 | 59 | .. code:: shell 60 | 61 | unzip Ansys-Python-Manager_*.zip 62 | ./installer.sh 63 | 64 | #. Search for the ``Ansys Python Manager`` and run it. 65 | 66 | The ``Ansys Python Manager`` window should appear at this stage. 67 | 68 | To uninstall the ``Ansys Python Manager``, follow below steps. 69 | 70 | #. Go to File menu. Click Uninstall option. 71 | 72 | #. In the pop up window: 73 | 74 | * If you want to remove all virtual environments which were created by 75 | the Ansys Python Manager as part of uninstallation, mark 76 | ``Delete virtual environments`` checkbox 77 | 78 | * If you want to remove all configurations as part of 79 | uninstallation, mark ``Delete configurations`` checkbox 80 | 81 | * If you want to remove all Python installations which were installed by 82 | the Ansys Python Manager as part of uninstallation, mark 83 | ``Delete Python installations`` checkbox 84 | 85 | #. Click ``Uninstall`` button. 86 | 87 | 88 | .. tab-item:: CentOS9, RHEL9 89 | 90 | Prerequisites: 91 | 92 | #. **OS** supported for **CentOS9** and **RHEL9**. 93 | 94 | #. Update ``yum`` repository and install the following packages with **sudo** privileges: 95 | **wget, gnome-terminal, Development Tools, libffi-devel, openssl-devel, rpm-build, sqlite-devel, sqlite-libs, libXinerama-devel, coreutils** 96 | 97 | .. code:: shell 98 | 99 | sudo yum update -y; 100 | sudo yum groupinstall 'Development Tools' -y; 101 | sudo yum install wget gnome-terminal libffi-devel openssl-devel rpm-build sqlite-devel sqlite-libs libXinerama-devel coreutils -y; 102 | 103 | #. Install **zlib** package using **wget** 104 | 105 | .. code:: shell 106 | 107 | sudo yum install wget -y 108 | wget https://zlib.net/current/zlib.tar.gz 109 | tar xvzf zlib.tar.gz 110 | cd zlib-* 111 | make clean 112 | ./configure 113 | make 114 | sudo make install 115 | 116 | To install the ``Ansys Python Manager``, follow below steps. 117 | 118 | #. Download the necessary installer from the `latest available release `_. 119 | The file should be named ``Ansys-Python-Manager_linux_centos_*.zip``. 120 | 121 | #. Execute the below command on the terminal 122 | 123 | .. code:: shell 124 | 125 | unzip Ansys-Python-Manager_linux_centos_*.zip 126 | ./installer_CentOS.sh 127 | 128 | #. Search for the ``Ansys Python Manager`` and run it. 129 | 130 | The ``Ansys Python Manager`` window should appear at this stage. 131 | 132 | To uninstall the ``Ansys Python Manager``, follow below steps. 133 | 134 | #. Go to File menu. Click Uninstall option. 135 | 136 | #. In the pop up window: 137 | 138 | * If you want to remove all virtual environments which were created by 139 | the Ansys Python Manager as part of uninstallation, mark 140 | ``Delete virtual environments`` checkbox 141 | 142 | * If you want to remove all configurations as part of 143 | uninstallation, mark ``Delete configurations`` checkbox 144 | 145 | * If you want to remove all Python installations which were installed by 146 | the Ansys Python Manager as part of uninstallation, mark 147 | ``Delete Python installations`` checkbox 148 | 149 | #. Click ``Uninstall`` button. 150 | 151 | #. Follow the uninstaller script & provide sudo permission to uninstall the application. 152 | 153 | .. tab-item:: Fedora39 154 | 155 | Prerequisites: 156 | 157 | #. **OS** supported for **Fedora39**. 158 | 159 | #. Update ``yum`` repository and install the following packages with **sudo** privileges: 160 | **wget, gnome-terminal, Development Tools, libffi-devel, openssl-devel, rpm-build, sqlite-devel, sqlite-libs, libXinerama-devel, coreutils** 161 | 162 | .. code:: shell 163 | 164 | sudo yum update -y; 165 | sudo yum groupinstall 'Development Tools' -y; 166 | sudo yum install wget gnome-terminal libffi-devel openssl-devel rpm-build sqlite-devel sqlite-libs libXinerama-devel coreutils -y; 167 | 168 | #. Install **zlib** package using **wget** 169 | 170 | .. code:: shell 171 | 172 | sudo yum install wget -y 173 | wget https://zlib.net/current/zlib.tar.gz 174 | tar xvzf zlib.tar.gz 175 | cd zlib-* 176 | make clean 177 | ./configure 178 | make 179 | sudo make install 180 | 181 | To install the ``Ansys Python Manager``, follow below steps. 182 | 183 | #. Download the necessary installer from the `latest available release `_. 184 | The file should be named ``Ansys-Python-Manager_linux_fedora_*.zip``. 185 | 186 | #. Execute the below command on the terminal 187 | 188 | .. code:: shell 189 | 190 | unzip Ansys-Python-Manager_linux_fedora_*.zip 191 | ./installer_Fedora.sh 192 | 193 | #. Search for the ``Ansys Python Manager`` and run it. 194 | 195 | The ``Ansys Python Manager`` window should appear at this stage. 196 | 197 | To uninstall the ``Ansys Python Manager``, follow below steps. 198 | 199 | #. Go to File menu. Click Uninstall option. 200 | 201 | #. In the pop up window: 202 | 203 | * If you want to remove all virtual environments which were created by 204 | the Ansys Python Manager as part of uninstallation, mark 205 | ``Delete virtual environments`` checkbox 206 | 207 | * If you want to remove all configurations as part of 208 | uninstallation, mark ``Delete configurations`` checkbox 209 | 210 | * If you want to remove all Python installations which were installed by 211 | the Ansys Python Manager as part of uninstallation, mark 212 | ``Delete Python installations`` checkbox 213 | 214 | #. Click ``Uninstall`` button. 215 | 216 | #. Follow the uninstaller script & provide sudo permission to uninstall the application. 217 | 218 | 219 | Installing Python 220 | ================= 221 | 222 | Now, instructions on how to install Python from the ``Ansys Python Manager`` are provided. 223 | 224 | In order to do so, just follow the upcoming steps: 225 | 226 | #. Search for the ``Ansys Python Manager`` and run it. 227 | 228 | #. Go to the ``Install Python`` tab, and select your desired Python install, version and extra packages. 229 | 230 | #. And follow the install process. 231 | 232 | 233 | Configurable options for the installer 234 | -------------------------------------- 235 | 236 | Two Python options for installation are available: 237 | 238 | * ``Standard``: this mode installs the standard Python version from `python.org `_ 239 | * ``Conda (miniforge)``: this mode installs the Python version from `miniforge `_. 240 | This install is characterized for being a modified ``conda`` install in which you have access to the ``conda`` 241 | package manager through the ``conda-forge`` channel. 242 | 243 | Regarding the available Python versions, users can select among the following ones: 244 | 245 | * Python 3.10 246 | * Python 3.11 247 | * Python 3.12 248 | 249 | .. collapse:: Linux : Python installation 250 | 251 | 1. Conda python installation: 252 | 253 | #. Bash scripts will be downloaded and executed on a machine directly from the official website.(https://github.com/conda-forge/miniforge?tab=readme-ov-file). 254 | 255 | 2. Standard python installation happens in two ways: 256 | 257 | #. If the Debian version is 22.04 and Python 3.11 (recommended by Ansys) is specified, the installer will 258 | automatically install the pre-compiled version of Python available within the installer. 259 | 260 | #. Otherwise, Python will be installed following these steps: 261 | 262 | #. Download Python Tarball and Untar: 263 | 264 | i. The Python tar file will be downloaded from the Python FTP server (https://www.python.org/ftp/python) 265 | based on the version selected from the dropdown menu. Example: For Python version 3.12.0, the download link 266 | would be here(https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tar.xz). 267 | 268 | ii. Decompress the downloaded file in the user’s cache directory. 269 | 270 | * Configure the Source: 271 | 272 | i. Following will be executed configure the installation: 273 | 274 | .. code:: shell 275 | 276 | ./configure --prefix=~/.local/ansys/{python_folder_name} 277 | 278 | * Build and install Python: 279 | 280 | i. Build and install Python using the make and make install commands. 281 | 282 | 283 | .. warning:: 284 | 285 | In the case of having selected ``Conda (miniforge)``, only Python 3.10 is available. 286 | 287 | Create Python virtual environment 288 | ================================= 289 | 290 | #. Search for the ``Ansys Python Manager`` and run it. 291 | 292 | #. Access the ``Create Python Environments`` tab. 293 | 294 | #. Select your desired ``Python version`` from the listed options. 295 | 296 | #. Provide the name of the virtual environment in the ``Enter virtual environment name`` text box. 297 | 298 | #. Finally, Click ``Create`` button to create. 299 | 300 | By default, Ansys Python Manager create virtual environment under, 301 | 302 | * ``{user directory}/.ansys_python_venvs`` for Windows 303 | * ``{user directory}/.local/ansys/.ansys_python_venvs`` for Linux 304 | 305 | To configure the default virtual environment creation path, go to the ``File >> Configure`` section 306 | ``(Ctrl + D)`` and provide your preferred path under the first text box. Then, click the ``Save`` button. 307 | 308 | 309 | Managing Python environments 310 | ============================ 311 | 312 | Through the ``Ansys Python Manager``, users can also have access to their different Python 313 | installations. Have a look at how to access it here: 314 | 315 | #. Search for the ``Ansys Python Manager`` and run it. 316 | #. Access the ``Manage Python Environments`` tab. 317 | #. Select your desired ``Python`` environment and start one of the listed options. 318 | 319 | By default, Ansys Python Manager list python environments available under, 320 | 321 | * ``{user directory}/.ansys_python_venvs`` for Windows 322 | * ``{user directory}/.local/ansys/.ansys_python_venvs`` for Linux 323 | 324 | To manage this directory, go to the ``File >> Configure`` section ``(Ctrl + D)`` and make the appropriate changes. 325 | 326 | #. To add a new default directory path, provide the path in the corresponding text box. 327 | #. To add a new path where virtual environments are searched for, provide the path in the corresponding text box and click the ``Add`` button. 328 | #. To remove directory path select the respective path that you want remove from the dropdown and click the ``Remove`` button. 329 | #. Finally, click the ``Save`` button to save the configurations. 330 | 331 | On the ``Launching options`` section, the following options are available: 332 | 333 | * ``Launch Console``: this option starts a console window with the command ``python`` pointing 334 | towards your selected Python environment. 335 | * ``Launch VSCode``: this option starts a ``Visual Studio Code``. If ``Visual Studio Code`` is 336 | not installed, then the ``Ansys Python Manager`` provides instructions to install it. 337 | * ``Launch JupyterLab``: this option starts a ``JupyterLab`` session. If ``JupyterLab`` is 338 | not installed, then the ``Ansys Python Manager`` installs it for you. 339 | * ``Launch Jupyter Notebook``: this option starts a ``Jupyter Notebook`` session. If 340 | ``Jupyter Notebook`` is not installed, then the ``Ansys Python Manager`` installs it for you. 341 | * ``Launch Spyder``: this option starts a Spyder IDE session. If Spyder is not installed, 342 | then the ``Ansys Python Manager`` installs it for you. 343 | 344 | On the ``Package management`` section, the following options are available: 345 | 346 | * ``Install Python default packages``: by selecting this option, your selected Python install 347 | receives the latest compatible versions for ``numpy``, ``scipy``, ``pandas``, ``matplotlib``, ``pyvista``, 348 | and ``scikit-learn``. 349 | * ``Install PyAnsys``: by selecting this option, your selected Python install has access to 350 | the latest, compatible PyAnsys metapackage installation. This metapackage provides you with 351 | access to the latest public PyAnsys libraries in their compatible version with the latest 352 | Ansys products. 353 | * ``List installed packages``: by selecting this option, a list of the installed packages on 354 | your selected Python install is provided. This might be useful for identifying potential problems. 355 | -------------------------------------------------------------------------------- /doc/source/user_guide.rst: -------------------------------------------------------------------------------- 1 | User guide 2 | ########## 3 | 4 | Introduction 5 | ============ 6 | 7 | Python has emerged as a leading language for a diverse range of 8 | applications, encompassing data science, machine learning, web 9 | development, and workflow automation. PyAnsys is a set of Python package 10 | that aligns with this trend, offering a Pythonic interface to Ansys 11 | Products. It serves as a powerful tool for automating engineering 12 | workflows, conducting parametric studies, and post-processing simulation 13 | results, among other applications. However, configuring Python 14 | environments and handling dependencies can occasionally prove to be 15 | daunting. This is where Ansys Python Manager comes to play. 16 | 17 | What is the Ansys Python Manager? 18 | ================================= 19 | 20 | The Ansys Python Manager is a collaborative open source Python QT 21 | app developed by various groups within Ansys. This efficient 22 | tool has garnered significant popularity and received positive feedback 23 | for its capability to streamline Python usage, simplify virtual 24 | environment management, and facilitate PyAnsys packages administration. 25 | 26 | In this user guide, various features offered by the Ansys Python Manager are explored, 27 | such as Python installation, creation and management of virtual environments, 28 | and the available ways to initiate Integrated Development Environments (IDEs) 29 | & Development environments. This user guide further explore the package management capabilities 30 | of the Ansys Python Manager, including the installation of PyAnsys packages and the 31 | management of dependencies. Finally, this user guide offers information on accessing 32 | PyAnsys documentation through the Ansys Python Manager, as well as the options available 33 | for updating the app. 34 | 35 | Installation 36 | ============ 37 | 38 | To install the Ansys Python Manager, you can visit its 39 | `repository `__ 40 | and download the installer from the latest release 41 | (``Ansys-Python-Manager-Setup-v*.exe``) from the Assets section. After 42 | downloading the installer, simply run it and follow the installation 43 | instructions to install the Ansys Python Manager on your machine. Once 44 | the installation is complete, you can easily launch the Ansys Python 45 | Manager from the Start Menu. 46 | 47 | Install python tab 48 | ================== 49 | 50 | The Ansys Python Manager offers a user-friendly interface for Python 51 | installation. You can easily select the desired Python version from the 52 | provided drop-down menu. The manager provides two options: standard 53 | Python and conda-forge python. The standard Python option is the 54 | official Python distribution, while the conda-forge Python option uses the 55 | community-driven distribution used with products like Anaconda. 56 | 57 | After selecting the desired Python version and clicking the Install 58 | button in the Ansys Python Manager, then the app proceeds to 59 | download and install the chosen Python distribution. The installation 60 | process may take a few minutes to complete. Once the installation is 61 | finished, the Ansys Python Manager subsequently displays the installed Python 62 | version and its corresponding installation path in the next tab labeled 63 | “Create Virtual Environments.” 64 | 65 | .. image:: _static/ansys_python_manager.PNG 66 | :align: center 67 | :height: 650 68 | :width: 560 69 | :alt: Screenshot of Ansys Python Manager 70 | 71 | 72 | Create virtual environments tab 73 | =============================== 74 | 75 | The “Create Virtual Environments” tab in the Ansys Python Manager 76 | provides the interface for creating and managing virtual environments. 77 | You can easily create a virtual environment by selecting the desired 78 | Python version and providing a name for the environment. Simply click 79 | the Create button to initiate the virtual environment creation process. 80 | The Ansys Python Manager creates the virtual environment and display 81 | its corresponding path in the subsequent tab labeled “Manage Virtual 82 | Environments.” 83 | 84 | .. image:: _static/create_venv_tab.PNG 85 | :align: center 86 | :height: 650 87 | :width: 560 88 | :alt: Screenshot of Create Virtual Environments Tab 89 | 90 | Manage virtual environments tab 91 | =============================== 92 | 93 | The “Manage Virtual Environments” tab in the Ansys Python Manager allows 94 | you to effectively manage the virtual environments you have created. You 95 | have options available, such as deleting a virtual environment, Launch 96 | Options, General Package Management, and PyAnsys Package Management for 97 | the selected virtual environment. You can also delete a virtual 98 | environment by selecting it from the available list under “Available 99 | virtual environments” and clicking on the “Delete virtual environments” 100 | option available with context menu. 101 | 102 | .. image:: _static/manage_venv_tab.PNG 103 | :align: center 104 | :height: 650 105 | :width: 560 106 | :alt: Screenshot of Manage Virtual Environments Tab 107 | 108 | ``Launch options`` provides the option to launch the selected virtual 109 | environment with the available IDEs and development environments. For 110 | example, you can launch the Spyder IDE by selecting the virtual 111 | environment from the available list and clicking on the 112 | ``Launch Spyder`` option. Likewise, you can launch the Jupyter Notebook, 113 | Jupyter Lab and console. 114 | 115 | .. image:: _static/launch_options.PNG 116 | :align: center 117 | :alt: Screenshot of Launch Options 118 | 119 | **Tip:** By using ``Launch console`` option, you can launch the 120 | virtual environment in the command prompt and subsequently navigate 121 | to the development folder and launch the VS Code IDEs using command 122 | line option “``code .``” And set the interpreter to the virtual 123 | environment. 124 | 125 | General package management 126 | ========================== 127 | 128 | In the "General Package Management" section, users are presented with the choice 129 | to "install default packages." By opting for this selection, the chosen 130 | Python installation or virtual environment receives the most up-to-date 131 | compatible versions of essential packages such as such as ``numpy``, ``scipy``, 132 | ``pandas``, ``matplotlib``, ``pyvista``, and ``scikit-learn``. 133 | After the installation process concludes, users can view the list of 134 | installed packages by clicking the ``List Installed Packages`` button. 135 | The Ansys Python Manager subsequently display the installed packages in the 136 | console, providing an overview of the installed dependencies. 137 | 138 | .. image:: _static/general_pkg_management.PNG 139 | :align: center 140 | :alt: Screenshot of General Package Management 141 | 142 | PyAnsys package management 143 | ========================== 144 | 145 | In the “PyAnsys Package Management” section, there is flexibility to 146 | choose the PyAnsys metapackage and its version to install it within the 147 | selected Python installation or virtual environment. By selecting the 148 | desired PyAnsys metapackage version from the available drop-down menu, 149 | users can proceed to install it by clicking the Install button. The 150 | Ansys Python Manager, then initiate the download and installation 151 | process of the PyAnsys metapackage. Individual PyAnsys packages are also 152 | available for download. It is important to note that the installation 153 | process may take a few minutes to complete, depending on the size of the 154 | package and the internet bandwidth. 155 | 156 | .. image:: _static/pyansys_pkg_management.PNG 157 | :align: center 158 | :alt: Screenshot of PyAnsys Package Management 159 | 160 | To ensure that you have the latest version of the Ansys Python Manager, 161 | you can easily update it by clicking on the “Check for Updates” 162 | option located in the File menu. The Ansys Python Manager, then 163 | verify if a newer version is available and update accordingly if 164 | necessary. 165 | 166 | In addition, the Ansys Python Manager offers convenient access to the 167 | ``PyAnsys documentation`` through the Help menu. By selecting your 168 | desired PyAnsys project from the provided drop-down menu and clicking on 169 | ``Open Website``, which redirected to the documentation website 170 | specifically to the selected project. This enables you to access 171 | comprehensive documentation and resources for your chosen PyAnsys 172 | project. 173 | 174 | .. image:: _static/pyansys_documentation.PNG 175 | :align: center 176 | :height: 367 177 | :width: 532 178 | :alt: Screenshot of PyAnsys Documentation Option 179 | 180 | Conclusion 181 | ========== 182 | 183 | In this comprehensive user guide, the wide range of features offered by 184 | the impressive Ansys Python Manager has been explored. This robust 185 | tool provides a user-friendly interface for effortless Python 186 | installation, efficient creation and management of virtual environments, 187 | and seamless installation of PyAnsys packages. Moreover, it offers 188 | multiple options for launching your virtual environment in various IDEs 189 | and development environments. By harnessing the power of the “Ansys 190 | Python Manager,” you can streamline your Python development workflow. 191 | So, what are you waiting for? Download the Ansys Python Manager and 192 | supercharge your PyAnsys development today. 193 | 194 | References 195 | ========== 196 | 197 | - `Ansys Python Manager 198 | Releases `__ 199 | - `Ansys Python Manager GitHub 200 | Repository `__ 201 | - `Ansys Python Manager 202 | Documentation `__ -------------------------------------------------------------------------------- /doc/styles/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !config 3 | !config/vocabularies 4 | !config/vocabularies/ANSYS 5 | !config/vocabularies/ANSYS/** 6 | !.gitignore -------------------------------------------------------------------------------- /doc/styles/config/vocabularies/ANSYS/accept.txt: -------------------------------------------------------------------------------- 1 | ANSYS 2 | Ansys 3 | Ansys Python Manager 4 | ansys 5 | metapackage 6 | miniforge 7 | PyAnsys 8 | Python 9 | python 10 | Spyder 11 | Windows 12 | -------------------------------------------------------------------------------- /doc/styles/config/vocabularies/ANSYS/reject.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/doc/styles/config/vocabularies/ANSYS/reject.txt -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | -------------------------------------------------------------------------------- /frozen.spec: -------------------------------------------------------------------------------- 1 | """pyinstaller app generation""" 2 | import glob 3 | import os 4 | import sys 5 | 6 | from PyInstaller.utils.hooks import collect_all, collect_submodules, copy_metadata 7 | from ansys.tools.path.misc import is_linux 8 | 9 | block_cipher = None 10 | 11 | # path where this file is located 12 | try: 13 | THIS_PATH = os.path.dirname(__file__) 14 | except NameError: 15 | THIS_PATH = os.getcwd() 16 | 17 | OUT_PATH = 'ansys_python_manager' 18 | APP_NAME = 'ansys_python_manager' if is_linux() else 'Ansys Python Manager' 19 | 20 | INSTALLER_PATH = os.path.join(THIS_PATH, 'src/ansys/tools/installer') 21 | ASSETS_PATH = os.path.join(INSTALLER_PATH, 'assets') 22 | ICON_FILE = os.path.join(ASSETS_PATH, 'pyansys_icon.ico') 23 | 24 | # consider testing paths 25 | main_py = os.path.join(THIS_PATH, 'src/ansys/tools/installer/__main__.py') 26 | 27 | if not os.path.isfile(main_py): 28 | raise FileNotFoundError(f'Unable to locate main entrypoint at {main_py}') 29 | 30 | added_files = [ 31 | (os.path.join(ASSETS_PATH, 'pyansys-light.png'), 'assets'), 32 | (os.path.join(ASSETS_PATH, 'ansys-favicon.png'), 'assets'), 33 | (os.path.join(ASSETS_PATH, 'pyansys_icon.ico'), 'assets'), 34 | (os.path.join(INSTALLER_PATH, 'VERSION'), '.'), 35 | ] 36 | 37 | # Missing metadata 38 | added_files += copy_metadata('ansys-tools-path') 39 | 40 | if is_linux() and os.getenv("ADD_PYTHON_BINARIES","true") == "true": 41 | added_files +=[(os.path.join(ASSETS_PATH, 'python-asset'), 'assets')] 42 | 43 | if is_linux(): 44 | added_files +=[(os.path.join(ASSETS_PATH, 'scripts'), 'assets')] 45 | 46 | a = Analysis([main_py], 47 | pathex=[], 48 | binaries=[], 49 | datas=added_files, 50 | hiddenimports=['_cffi_backend'], 51 | hookspath=[], 52 | runtime_hooks=[], 53 | excludes=[], 54 | win_no_prefer_redirects=False, 55 | win_private_assemblies=False, 56 | cipher=block_cipher, 57 | noarchive=False) 58 | 59 | # a.datas += [('.ico', icon_file, 'DATA')] 60 | 61 | pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) 62 | 63 | exe = EXE(pyz, 64 | a.scripts, 65 | [], 66 | exclude_binaries=True, 67 | name=APP_NAME, 68 | debug=False, 69 | bootloader_ignore_signals=False, 70 | strip=False, 71 | upx=True, 72 | console=False, 73 | icon=ICON_FILE) 74 | 75 | coll = COLLECT(exe, 76 | a.binaries, 77 | a.zipfiles, 78 | a.datas, 79 | strip=False, 80 | upx=True, 81 | name=OUT_PATH) 82 | -------------------------------------------------------------------------------- /images/app-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/images/app-image.png -------------------------------------------------------------------------------- /linux/debian/ansys_python_manager_prebuid.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | 3 | # The type of the thing this desktop file refers to (e.g. can be Link) 4 | Type=Application 5 | 6 | # The application name. 7 | Name=Ansys Python Manager 8 | 9 | # Tooltip comment to show in menus. 10 | Comment=Ansys Python Manager. 11 | 12 | # The path (folder) in which the executable is run 13 | Path=${HOME}/.local/opt/ansys_python_manager 14 | 15 | # The executable (can include arguments) 16 | Exec=${HOME}/.local/opt/ansys_python_manager/ansys_python_manager 17 | 18 | # The icon for the entry, using the name from `hicolor/scalable` without the extension. 19 | # You can also use a full path to a file in /opt. 20 | Icon=${HOME}/.local/share/icons/pyansys_icon.svg -------------------------------------------------------------------------------- /linux/debian/fpm-options-debian: -------------------------------------------------------------------------------- 1 | -d "libffi-dev" 2 | -d "libssl-dev" 3 | -d "build-essential" 4 | -d "libsqlite3-dev" 5 | -d "libxcb-xinerama0" 6 | -C package 7 | -s dir 8 | -t deb 9 | -n "ansys_python_manager" 10 | -p ansys_python_manager.deb 11 | -n "ansys_python_manager" 12 | --description "Ansys application to manage Python on the workstation." 13 | --url "https://installer.docs.pyansys.com/version/dev/installer.html" 14 | --maintainer "ANSYS, Inc. " -------------------------------------------------------------------------------- /linux/debian/installer.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | clear 3 | printf """Installation started.....\n""" 4 | missing_deps=() 5 | dependencies_available=true 6 | # check zlib 7 | ls /usr/local/lib/libz.so >/dev/null 2>&1 8 | ret=$? 9 | if [ $ret -eq 0 ]; then 10 | : 11 | else 12 | missing_deps+=("zlib") 13 | dependencies_available=false 14 | fi 15 | # check other dependencies 16 | arr=("wget" "gnome" "libffi-dev" "libssl-dev" "build-essential" "libsqlite3-dev" "libxcb-xinerama0") 17 | for x in "${arr[@]}"; do 18 | c="dpkg -s $x >/dev/null 2>&1" 19 | eval $c 20 | ret=$? 21 | if [ $ret -eq 0 ]; then 22 | : 23 | else 24 | missing_deps+=("$x") 25 | dependencies_available=false 26 | fi 27 | done 28 | if [ $dependencies_available = true ]; then 29 | dpkg -x ./ansys_python_manager.deb ${HOME}/.local 30 | ./postInstallScript.sh 31 | available=$(cat ~/.bashrc | grep -zoP "# Add alias for Ansys Python Manager \nalias ansys_python_manager=~/.local/opt/ansys_python_manager/ansys_python_manager" | wc -l) 32 | echo $available 33 | if [ $available -lt 1 ] 34 | then 35 | echo -e "# Add alias for Ansys Python Manager \nalias ansys_python_manager=~/.local/opt/ansys_python_manager/ansys_python_manager" >> ~/.bashrc 36 | fi 37 | printf "\nInstallation successful. \nIt is suggested to restart your machine to begin using the software....\n" 38 | else 39 | echo "Missing dependencies..." 40 | while true; do 41 | read -p "Require sudo permission to install dependencies. Do you want to install?(Y/N): " user_selection 42 | if [ "$user_selection" = "Y" ] || [ "$user_selection" = "N" ]; then 43 | break 44 | fi 45 | clear 46 | done 47 | printf "\n" 48 | if [ "$user_selection" = "Y" ]; then 49 | sudo -v >/dev/null 2>&1 50 | root_check=$? 51 | if [ $root_check -eq 0 ]; then 52 | install_script="sudo apt-get update -y; " 53 | install_zlib=false 54 | for x in "${missing_deps[@]}"; do 55 | if [ $x == "zlib" ]; then 56 | install_zlib=true 57 | else 58 | install_script="$install_script sudo apt-get install $x -y;" 59 | fi 60 | done 61 | if [ $install_zlib = true ]; then 62 | install_script="$install_script rm -rf ansys-prereq/ ; mkdir -p ansys-prereq; cd ansys-prereq; wget https://zlib.net/current/zlib.tar.gz; tar xvzf zlib.tar.gz; cd zlib-*; make clean; ./configure; make; sudo make install; cd ../..; rm -rf ansys-prereq;" 63 | fi 64 | 65 | dependencies_available=true 66 | eval $install_script 67 | # Confirmation 68 | # check zlib 69 | ls /usr/local/lib/libz.so >/dev/null 2>&1 70 | ret=$? 71 | if [ $ret -eq 0 ]; then 72 | : 73 | else 74 | missing_deps+=("zlib") 75 | dependencies_available=false 76 | fi 77 | # check other dependencies 78 | arr=("wget" "gnome" "libffi-dev" "libssl-dev" "build-essential" "libsqlite3-dev" "libxcb-xinerama0") 79 | for x in "${arr[@]}"; do 80 | c="dpkg -s $x >/dev/null 2>&1" 81 | eval $c 82 | ret=$? 83 | if [ $ret -eq 0 ]; then 84 | : 85 | else 86 | missing_deps+=("$x") 87 | dependencies_available=false 88 | fi 89 | done 90 | if [ $dependencies_available = false ]; then 91 | echo "Unable to install dependencies. Check above logs and try again..." 92 | else 93 | dpkg -x ./ansys_python_manager.deb ${HOME}/.local 94 | ./postInstallScript.sh 95 | available=$(cat ~/.bashrc | grep -zoP "# Add alias for Ansys Python Manager \nalias ansys_python_manager=~/.local/opt/ansys_python_manager/ansys_python_manager" | wc -l) 96 | echo $available 97 | if [ $available -lt 1 ] 98 | then 99 | echo -e "# Add alias for Ansys Python Manager \nalias ansys_python_manager=~/.local/opt/ansys_python_manager/ansys_python_manager" >> ~/.bashrc 100 | fi 101 | printf "\nInstallation successful. \nIt is suggested to restart your machine to begin using the software....\n" 102 | fi 103 | else 104 | echo "You don't have access to sudo. Please try again..." 105 | fi 106 | else 107 | printf "Install below mentioned dependencies to proceed installation.... \n" 108 | for x in "${missing_deps[@]}"; do 109 | echo "$x" 110 | done 111 | printf "Dependencies installation required sudo access.\n" 112 | echo -e '\e]8;;https://installer.docs.pyansys.com/version/stable/installer.html\aFollow prerequisites in this link\e]8;;\a' 113 | fi 114 | fi -------------------------------------------------------------------------------- /linux/debian/postInstallScript.sh: -------------------------------------------------------------------------------- 1 | envsubst < ${HOME}/.local/share/applications/ansys_python_manager_prebuid.desktop > ${HOME}/.local/share/applications/ansys_python_manager.desktop 2 | rm -rf ${HOME}/.local/share/applications/ansys_python_manager_prebuid.desktop -------------------------------------------------------------------------------- /linux/non-debian/ansys_python_manager.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | 3 | # The type of the thing this desktop file refers to (e.g. can be Link) 4 | Type=Application 5 | 6 | # The application name. 7 | Name=Ansys Python Manager 8 | 9 | # Tooltip comment to show in menus. 10 | Comment=Ansys Python Manager. 11 | 12 | # The path (folder) in which the executable is run 13 | Path=/opt/ansys_python_manager 14 | 15 | # The executable (can include arguments) 16 | Exec=/opt/ansys_python_manager/ansys_python_manager 17 | 18 | # The icon for the entry, using the name from `hicolor/scalable` without the extension. 19 | # You can also use a full path to a file in /opt. 20 | Icon=pyansys_icon -------------------------------------------------------------------------------- /linux/non-debian/fpm-options-centos: -------------------------------------------------------------------------------- 1 | -C package_CentOS 2 | -s dir 3 | -t rpm 4 | -n "ansys_python_manager" 5 | -p ansys_python_manager_CentOS.rpm 6 | -n "ansys_python_manager" 7 | --description "Ansys application to manage Python on the workstation." 8 | --url "https://installer.docs.pyansys.com/version/dev/installer.html" 9 | --maintainer "ANSYS, Inc. " -------------------------------------------------------------------------------- /linux/non-debian/fpm-options-fedora: -------------------------------------------------------------------------------- 1 | -C package_Fedora 2 | -s dir 3 | -t rpm 4 | -n "ansys_python_manager" 5 | -p ansys_python_manager_Fedora.rpm 6 | -n "ansys_python_manager" 7 | --description "Ansys application to manage Python on the workstation." 8 | --url "https://installer.docs.pyansys.com/version/dev/installer.html" 9 | --maintainer "ANSYS, Inc. " -------------------------------------------------------------------------------- /linux/non-debian/installer_CentOS.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | clear 3 | missing_deps=() 4 | dependencies_available=true 5 | # Check user input for sudo permission 6 | while true; do 7 | read -p "Require sudo permission to install this package and the dependencies. Do you want to continue?(Y/N): " user_selection 8 | if [ "$user_selection" = "Y" ] || [ "$user_selection" = "N" ]; then 9 | break 10 | fi 11 | clear 12 | done 13 | printf "\n" 14 | if [ "$user_selection" = "Y" ]; then 15 | #Check sudo 16 | sudo -v >/dev/null 2>&1 17 | root_check=$? 18 | if [ $root_check -eq 0 ]; then 19 | # check zlib 20 | ls /usr/local/lib/libz.so >/dev/null 2>&1 21 | ret=$? 22 | if [ $ret -eq 0 ]; then 23 | : 24 | else 25 | missing_deps+=("zlib") 26 | dependencies_available=false 27 | fi 28 | # check other dependencies 29 | yum grouplist | grep 'Development Tools' 30 | ret=$? 31 | if [ $ret -eq 0 ]; then 32 | : 33 | else 34 | missing_deps+=("Development Tools") 35 | dependencies_available=false 36 | fi 37 | arr=("wget" "gnome-terminal" "libffi-devel" "openssl-devel" "rpm-build" "sqlite-devel" "sqlite-libs" "libXinerama-devel" "coreutils") 38 | for x in "${arr[@]}"; do 39 | c="rpm -qa | grep $x" 40 | eval $c 41 | ret=$? 42 | if [ $ret -eq 0 ]; then 43 | : 44 | else 45 | missing_deps+=("$x") 46 | dependencies_available=false 47 | fi 48 | done 49 | if [ $dependencies_available = true ]; then 50 | sudo rpm -iv ansys_python_manager_*.rpm 51 | available=$(cat ~/.bashrc | grep -zoP "# Add alias for Ansys Python Manager \nalias ansys_python_manager=~/.local/opt/ansys_python_manager/ansys_python_manager" | wc -l) 52 | echo $available 53 | if [ $available -lt 1 ] 54 | then 55 | echo -e "# Add alias for Ansys Python Manager \nalias ansys_python_manager=~/.local/opt/ansys_python_manager/ansys_python_manager" | sudo tee -a ~/.bashrc 56 | fi 57 | printf "\nInstallation success...\nIt is suggested to restart your machine to begin using the software....\n" 58 | else 59 | echo "Missing dependencies..." 60 | install_script="sudo yum update -y; sudo yum install " 61 | install_zlib=false 62 | for x in "${missing_deps[@]}"; do 63 | if [ $x == "zlib" ]; then 64 | install_zlib=true 65 | else 66 | install_script="$install_script $x" 67 | fi 68 | done 69 | install_script="$install_script -y" 70 | if [ $install_zlib = true ]; then 71 | install_script="$install_script; mkdir ansys-prereq; cd ansys-prereq; wget https://zlib.net/current/zlib.tar.gz; tar xvzf zlib.tar.gz; cd zlib-*; sudo make clean; ./configure; sudo make; sudo make install; cd ../..; rm -rf ansys-prereq;" 72 | fi 73 | eval $install_script 74 | sudo rpm -iv ansys_python_manager_*.rpm 75 | printf "\nInstallation success...\n" 76 | fi 77 | else 78 | echo "You don't have access to sudo. Please try again..." 79 | fi 80 | else 81 | printf "Aborting installation....\nUser permission denied.... \n\n" 82 | echo "Ansys Python Manager and required dependencies require sudo access to install." 83 | fi 84 | -------------------------------------------------------------------------------- /linux/non-debian/updater_CentOS.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | while true; do 4 | read -p "Require sudo permission to update & install the package. Do you want to continue?(Y/N): " user_selection 5 | if [ "$user_selection" = "Y" ] || [ "$user_selection" = "N" ]; then 6 | break 7 | fi 8 | clear 9 | done 10 | if [ "$user_selection" = "Y" ]; then 11 | sudo rpm -iv ansys_python_manager_*.rpm 12 | else 13 | printf "Aborting installation....\nUser permission denied.... \n\n" 14 | echo "Ansys Python Manager and required dependencies require sudo access to install." 15 | fi -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Simple make.bat to simplify repetitive build env management tasks under Windows 6 | 7 | if "%1" == "install" goto install 8 | if "%1" == "tests" goto tests 9 | if "%1" == "doc" goto doc 10 | if "%1" == "build" goto build 11 | if "%1" == "clean" goto clean 12 | if "%1" == "fresh-build" goto fresh-build 13 | 14 | :setup 15 | Echo ^>^>^> Setting up environment... 16 | python -m pip install -U pip uv 17 | goto :eof 18 | 19 | :install 20 | call :setup 21 | Echo ^>^>^> Installing... 22 | uv pip install -e .[freeze] 23 | Echo ^>^>^> Installation complete. 24 | goto end 25 | 26 | :tests 27 | call :setup 28 | Echo ^>^>^> Installing test dependencies... 29 | uv pip install -e .[tests] 30 | Echo ^>^>^> Running tests... 31 | uv run pytest 32 | goto end 33 | 34 | :doc 35 | call :setup 36 | Echo ^>^>^> Installing documentation dependencies... 37 | uv pip install -e .[doc] 38 | Echo ^>^>^> Building documentation... 39 | chdir /d doc 40 | call make.bat clean 41 | call make.bat html 42 | chdir /d .. 43 | Echo ^>^>^> Documentation complete. 44 | goto end 45 | 46 | :build 47 | call :setup 48 | Echo ^>^>^> Freezing using pyinstaller 49 | uv run pyinstaller frozen.spec 50 | goto end 51 | 52 | :clean 53 | Echo ^>^>^> Cleaning up build files... 54 | rmdir /s /q build > /NUL 2>&1 55 | rmdir /s /q dist > /NUL 2>&1 56 | goto end 57 | 58 | :fresh-build 59 | call :clean 60 | call :build 61 | goto end 62 | 63 | :end 64 | popd 65 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core >=3.2,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "ansys-tools-installer" 7 | description = "Python QT app or CLI for installing Python and PyAnsys." 8 | readme = "README.rst" 9 | requires-python = ">=3.10,<4" 10 | license = { file = "LICENSE" } 11 | authors = [{ name = "Ansys, Inc.", email = "pyansys.core@ansys.com" }] 12 | maintainers = [{ name = "Ansys, Inc.", email = "pyansys.core@ansys.com" }] 13 | dependencies = [ 14 | "packaging", 15 | "PyGithub", 16 | "appdirs", 17 | "requests", 18 | "PySide6", 19 | "ansys-tools-path", 20 | "setuptools; python_version >= '3.12'", 21 | ] 22 | classifiers = [ 23 | "Development Status :: 4 - Beta", 24 | "Intended Audience :: Science/Research", 25 | "Topic :: Scientific/Engineering :: Information Analysis", 26 | "License :: OSI Approved :: MIT License", 27 | "Operating System :: Microsoft :: Windows", 28 | "Programming Language :: Python :: 3.10", 29 | "Programming Language :: Python :: 3.11", 30 | "Programming Language :: Python :: 3.12", 31 | ] 32 | dynamic = ["version"] 33 | 34 | 35 | [project.optional-dependencies] 36 | tests = [ 37 | "packaging==25.0", 38 | "PyGithub==2.6.1", 39 | "appdirs==1.4.4", 40 | "requests==2.32.3", 41 | "PySide6==6.9.0", 42 | "ansys-tools-path==0.7.1", 43 | "pytest==8.3.5", 44 | "pytest-cov==6.1.1", 45 | "pytest-qt==4.4.0", 46 | "setuptools==80.9.0", 47 | ] 48 | doc = [ 49 | "Sphinx==8.1.3", 50 | "ansys-sphinx-theme==1.5.0", 51 | "sphinx-copybutton==0.5.2", 52 | "sphinx_design==0.6.1", 53 | "sphinx_toolbox==4.0.0", 54 | ] 55 | freeze = [ 56 | "pyinstaller==6.13.0", 57 | "packaging==25.0", 58 | "PyGithub==2.6.1", 59 | "appdirs==1.4.4", 60 | "requests==2.32.3", 61 | "PySide6==6.9.0", 62 | "ansys-tools-path==0.7.1", 63 | ] 64 | 65 | [tool.flit.module] 66 | name = "ansys.tools.installer" 67 | 68 | [project.urls] 69 | Source = "https://github.com/ansys/python-installer-qt-gui" 70 | Issues = "https://github.com/ansys/python-installer-qt-gui/issues" 71 | Discussions = "https://github.com/ansys/python-installer-qt-gui/discussions" 72 | Documentation = "https://installer.docs.pyansys.com/" 73 | Releases = "https://github.com/ansys/python-installer-qt-gui/releases" 74 | 75 | [project.scripts] 76 | ansys_python_installer = "ansys.tools.installer:open_gui" 77 | 78 | [tool.pytest.ini_options] 79 | junit_family = "legacy" 80 | testpaths = "tests" 81 | qt_api = "pyside6" 82 | 83 | [tool.black] 84 | line-length = 100 85 | 86 | [tool.isort] 87 | profile = "black" 88 | force_sort_within_sections = true 89 | line_length = 100 90 | src_paths = ["doc", "src", "tests"] 91 | 92 | [tool.coverage.run] 93 | source = ["ansys.tools"] 94 | 95 | [tool.coverage.report] 96 | show_missing = true 97 | 98 | [tool.codespell] 99 | skip = '*.spec' 100 | ignore-words = "doc/styles/config/vocabularies/ANSYS/accept.txt" 101 | quiet-level = 3 102 | -------------------------------------------------------------------------------- /scripts/requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.32.3 2 | packaging==25.0 3 | -------------------------------------------------------------------------------- /scripts/update_python_versions.py: -------------------------------------------------------------------------------- 1 | """Script that updates the Python versions used in the project.""" 2 | 3 | import os 4 | import re 5 | 6 | from packaging.version import Version, parse 7 | import requests 8 | 9 | 10 | def is_version_string(s: str) -> bool: 11 | """Check if the string is in accepted version format. 12 | 13 | Parameters 14 | ---------- 15 | s : str 16 | String to check. 17 | 18 | Returns 19 | ------- 20 | bool 21 | True if the string is in the accepted version format, False otherwise. 22 | """ 23 | pattern = r"^\d+\.\d+\.\d+$" 24 | return bool(re.match(pattern, s)) 25 | 26 | 27 | def get_latest_github_release(user, repo) -> dict: 28 | """Get the latest release of a GitHub repository. 29 | 30 | Parameters 31 | ---------- 32 | user : str 33 | GitHub username. 34 | repo : str 35 | Repository name. 36 | 37 | Returns 38 | ------- 39 | dict 40 | JSON response of the latest release. 41 | """ 42 | url = f"https://api.github.com/repos/{user}/{repo}/releases/latest" 43 | response = requests.get(url) 44 | if response.status_code == 200: 45 | return response.json() 46 | else: 47 | print(f"Failed to get releases: {response.content}") 48 | return None 49 | 50 | 51 | def get_minor_version_sublist_with_greater_patch(list: list[str], current_version: str): 52 | """Get the sublist of versions with greater patch than the current version.""" 53 | major, minor, patch = current_version.split(".") 54 | major, minor, patch = int(major), int(minor), int(patch) 55 | sublist = [version for version in list if version.startswith(f"{major}.{minor}.")] 56 | sublist = [version for version in sublist if int(version.split(".")[2]) > patch] 57 | sublist = sorted(sublist, key=Version, reverse=True) 58 | 59 | return sublist 60 | 61 | 62 | # Get path to the root of the project 63 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 64 | 65 | # Path to the constants file 66 | CONSTANTS_FILE = os.path.join( 67 | ROOT_DIR, "src", "ansys", "tools", "installer", "constants.py" 68 | ) 69 | 70 | # Parse the constants file to find the current Python versions 71 | # used in the project. The versions are stored in a tuple 72 | with open(CONSTANTS_FILE, "r") as f: 73 | lines = f.readlines() 74 | 75 | # Import the following constants inside the constants file 76 | # 77 | # Example: 78 | # 79 | # VANILLA_PYTHON_VERSIONS = { 80 | # "Python 3.10": "3.10.11", 81 | # "Python 3.11": "3.11.6", 82 | # "Python 3.12": "3.12.0", 83 | # } 84 | # 85 | # CONDA_PYTHON_VERSION = "23.1.0-4" 86 | # 87 | 88 | vanilla_python_versions: dict[str, str] = {} 89 | conda_python_version: str = "" 90 | 91 | for line in lines: 92 | if "VANILLA_PYTHON_VERSIONS" in line: 93 | # Store the index of the line where the dictionary starts 94 | start_index = lines.index(line) 95 | break 96 | 97 | # Get the dictionary that contains the Python versions 98 | for line in lines[start_index:]: 99 | if "}" in line: 100 | # Store the index of the line where the dictionary ends 101 | end_index = lines.index(line, start_index) 102 | break 103 | 104 | # Parse the dictionary to get the Python versions 105 | for line in lines[start_index : end_index + 1]: 106 | if "Python" in line: 107 | # Extract the Python version and the version number 108 | python_version = line.split(":")[0].strip().replace('"', "") 109 | version_number = line.split(":")[1].strip().replace('"', "").replace(",", "") 110 | 111 | # Store the Python version and the version number 112 | vanilla_python_versions[python_version] = version_number 113 | 114 | # Get the Conda Python version 115 | for line in lines: 116 | if "CONDA_PYTHON_VERSION" in line: 117 | conda_python_version = line.split("=")[1].strip().replace('"', "") 118 | 119 | # LOG - Print the current Python versions 120 | print("Current Vanilla Python versions:") 121 | for version in vanilla_python_versions.values(): 122 | print(f">>> '{version}'") 123 | 124 | print("Current Conda Python version") 125 | print(f">>> '{conda_python_version}'") 126 | 127 | # -------------------------------------------------------------------------------------------- 128 | 129 | print("--- \nUpdating Python versions...\n") 130 | 131 | # Check remote Python versions available 132 | PYTHON_FTP = "https://www.python.org/ftp/python" 133 | 134 | # List all folders in the Python FTP 135 | response = requests.get(PYTHON_FTP) 136 | text = response.text.split("\n") 137 | ftp_versions = [] 138 | for line in text: 139 | tmp = line.strip('')[0] 140 | # Check if the folder is a Python version 141 | if is_version_string(tmp): 142 | ftp_versions.append(tmp) 143 | 144 | # For each minor version, get the patch versions available 145 | # greter than the current patch version 146 | for python_version_key, python_version_value in vanilla_python_versions.items(): 147 | # Get the minor version of the current Python version 148 | minor_version = ".".join(python_version_value.split(".")[:2]) 149 | 150 | # Get the patch versions available 151 | patch_versions = get_minor_version_sublist_with_greater_patch( 152 | ftp_versions, python_version_value 153 | ) 154 | 155 | # Check if the patch versions contain the executable 156 | new_patch_version = None 157 | for patch_version in patch_versions: 158 | # Check if the executable exists 159 | response_1 = requests.get( 160 | f"{PYTHON_FTP}/{patch_version}/Python-{patch_version}.tar.xz" 161 | ) 162 | response_2 = requests.get( 163 | f"{PYTHON_FTP}/{patch_version}/python-{patch_version}-amd64.exe" 164 | ) 165 | if response_1.status_code == 200 and response_2.status_code == 200: 166 | print(f"Python {patch_version} is available for download") 167 | new_patch_version = patch_version 168 | break 169 | 170 | # Update the Python version 171 | if new_patch_version: 172 | vanilla_python_versions[python_version_key] = new_patch_version 173 | else: 174 | print(f"Python {python_version_value} is already the latest version available") 175 | 176 | # Get the latest Conda Python version 177 | latest_conda_release = get_latest_github_release("conda-forge", "miniforge") 178 | 179 | # Verify that the assets are available 180 | assets = latest_conda_release["assets"] 181 | new_conda_version = None 182 | count = 0 183 | for asset in assets: 184 | if f"Miniforge3-{latest_conda_release['name']}-Linux-x86_64.sh" in asset["name"]: 185 | count += 1 186 | if f"Miniforge3-{latest_conda_release['name']}-Windows-x86_64.exe" in asset["name"]: 187 | count += 1 188 | if count == 2: 189 | new_conda_version = latest_conda_release["name"] 190 | break 191 | 192 | # Update the Conda Python version 193 | if new_conda_version: 194 | conda_python_version = new_conda_version 195 | print(f"Conda Python version updated to {conda_python_version}") 196 | else: 197 | print(f"Conda Python version is already the latest version available") 198 | 199 | print("\nPython versions updated successfully\n ---") 200 | 201 | # -------------------------------------------------------------------------------------------- 202 | 203 | # LOG - Print the new Python versions 204 | print("New Vanilla Python versions:") 205 | for version in vanilla_python_versions.values(): 206 | print(f">>> '{version}'") 207 | 208 | print("New Conda Python version:") 209 | print(f">>> '{conda_python_version}'") 210 | 211 | # Update the constants file with the new Python versions 212 | # Write the new Python versions to the constants file 213 | with open(CONSTANTS_FILE, "w") as f: 214 | for line in lines[:start_index]: 215 | f.write(line) 216 | 217 | f.write("VANILLA_PYTHON_VERSIONS = {\n") 218 | for python_version, version_number in vanilla_python_versions.items(): 219 | f.write(f' "{python_version}": "{version_number}",\n') 220 | f.write("}\n\n") 221 | 222 | f.write(f'CONDA_PYTHON_VERSION = "{conda_python_version}"\n') 223 | 224 | 225 | # Path for ci_cd.yaml 226 | YAML_FILE = os.path.join(ROOT_DIR, ".github", "workflows", "ci_cd.yml") 227 | 228 | # Read the file 229 | with open(YAML_FILE, "r") as f: 230 | yaml_contents = f.readlines() 231 | 232 | # Get pattern to replace 233 | py_version = parse(list(vanilla_python_versions.values())[-2]) 234 | py_version_search = f"{str(py_version.major)}.{str(py_version.minor)}" 235 | search_str = re.compile(py_version_search + r"\.[\d]{1,}") 236 | 237 | # Replace the version 238 | for n, yaml_line in enumerate(yaml_contents): 239 | if "PRECOMPILE_PYTHON_VERSION:" in yaml_line: 240 | yaml_contents[n] = search_str.sub(py_version.base_version, yaml_line) 241 | 242 | # Rewrite the yaml file 243 | with open(YAML_FILE, "w") as yaml: 244 | yaml.writelines(yaml_contents) 245 | -------------------------------------------------------------------------------- /setup.nsi: -------------------------------------------------------------------------------- 1 | ; NSIS script for Ansys Python Manager installer 2 | 3 | ; Set the name, version, and output path of the installer 4 | !define VERSION_FILE "src/ansys/tools/installer/VERSION" 5 | !define LICENSE_FILE "LICENSE" 6 | !define PRODUCT_NAME "Ansys Python Manager" 7 | !define /file PRODUCT_VERSION "src/ansys/tools/installer/VERSION" 8 | !define OUTFILE_NAME "Ansys-Python-Manager-Setup-v${PRODUCT_VERSION}.exe" 9 | 10 | Name "${PRODUCT_NAME}" 11 | VIProductVersion "${PRODUCT_VERSION}" 12 | OutFile "dist\${OUTFILE_NAME}" 13 | 14 | !define MULTIUSER_EXECUTIONLEVEL Highest 15 | !define MULTIUSER_MUI 16 | !define MULTIUSER_INSTALLMODE_COMMANDLINE 17 | !include MultiUser.nsh 18 | !include MUI2.nsh 19 | !include InstallOptions.nsh 20 | 21 | !define MUI_PAGE_CUSTOMFUNCTION_PRE oneclickpre 22 | !insertmacro MULTIUSER_PAGE_INSTALLMODE 23 | !insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}" 24 | !insertmacro MUI_PAGE_INSTFILES 25 | !include "uninstall.nsi" 26 | 27 | Function CreateDesktopShortCut 28 | CreateShortCut "$desktop\Ansys Python Manager.lnk" "$INSTDIR\Ansys Python Manager.exe" 29 | FunctionEnd 30 | 31 | !define MUI_FINISHPAGE_RUN "$INSTDIR\Ansys Python Manager.exe" 32 | !define MUI_FINISHPAGE_SHOWREADME 33 | !define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut" 34 | !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED 35 | !define MUI_FINISHPAGE_SHOWREADME_FUNCTION "CreateDesktopShortCut" 36 | !insertmacro MUI_PAGE_FINISH 37 | 38 | Function .onInit 39 | !insertmacro MULTIUSER_INIT 40 | FunctionEnd 41 | 42 | Function un.onInit 43 | !insertmacro MULTIUSER_UNINIT 44 | FunctionEnd 45 | 46 | ; Define the installer sections 47 | Section "Ansys Python Manager" SEC01 48 | ; Set the installation directory to the program files directory 49 | SetOutPath "$PROGRAMFILES64\ANSYS Inc\Ansys Python Manager" 50 | 51 | ; Copy the files from the dist\ansys_python_manager directory 52 | ; File /r /oname=ignore "dist\ansys_python_manager\*" 53 | File /r "dist\ansys_python_manager\*" 54 | 55 | ; Create the start menu directory 56 | CreateDirectory "$SMPROGRAMS\Ansys Python Manager" 57 | 58 | ; Create the start menu shortcut 59 | CreateShortCut "$SMPROGRAMS\Ansys Python Manager\Ansys Python Manager.lnk" "$INSTDIR\Ansys Python Manager.exe" 60 | 61 | ; Add the program to the installed programs list 62 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME}" 63 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" 64 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayIcon" "$\"$INSTDIR\Ansys Python Manager.exe$\"" 65 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "Publisher" "ANSYS Inc" 66 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "Version" "${PRODUCT_VERSION}" 67 | WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${PRODUCT_VERSION}" 68 | 69 | WriteUninstaller "$INSTDIR\uninstall.exe" 70 | 71 | SectionEnd 72 | 73 | ; Define the uninstaller section 74 | Section "Uninstall" SEC02 75 | 76 | Delete "$PROGRAMFILES64\Ansys Python Manager\*.*" 77 | RMDir "$PROGRAMFILES64\Ansys Python Manager" 78 | DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" 79 | Delete "$SMPROGRAMS\Ansys Python Manager\Ansys Python Manager.lnk" 80 | RMDir "$SMPROGRAMS\Ansys Python Manager" 81 | Delete "$desktop\Ansys Python Manager.lnk" 82 | SectionEnd 83 | 84 | Icon "dist\ansys_python_manager\_internal\assets\pyansys_icon.ico" 85 | InstallDir "$PROGRAMFILES64\ANSYS Inc\Ansys Python Manager" 86 | 87 | ; Define the custom functions for the MUI2 OneClick plugin 88 | InstProgressFlags smooth 89 | Function oneclickpre 90 | !insertmacro MUI_HEADER_TEXT "Installing ${PRODUCT_NAME}" "Please wait while the installation completes." 91 | ; !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" 92 | HideWindow 93 | FunctionEnd 94 | 95 | ; Call the MUI2 OneClick plugin 96 | !insertmacro MUI_UNPAGE_CONFIRM 97 | !insertmacro MUI_UNPAGE_INSTFILES 98 | !insertmacro MUI_LANGUAGE English 99 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/VERSION: -------------------------------------------------------------------------------- 1 | 0.5.dev0 -------------------------------------------------------------------------------- /src/ansys/tools/installer/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Ansys Python Manager.""" 24 | 25 | import os 26 | import sys 27 | import warnings 28 | 29 | from appdirs import user_cache_dir 30 | 31 | if getattr(sys, "frozen", False): 32 | # If the application is run as a bundle, the PyInstaller bootloader 33 | # extends the sys module by a flag frozen=True and sets the app 34 | # path into variable _MEIPASS'. 35 | try: 36 | _THIS_PATH = sys._MEIPASS 37 | except: 38 | # this might occur on a single file install 39 | os.path.dirname(sys.executable) 40 | else: 41 | _THIS_PATH = os.path.dirname(os.path.abspath(__file__)) 42 | 43 | # Read in version programmatically from plain text 44 | # this is done so NSIS can also link to the same version 45 | with open(os.path.join(_THIS_PATH, "VERSION")) as fid: 46 | __version__ = fid.read() 47 | 48 | CACHE_DIR = user_cache_dir("ansys_python_installer") 49 | 50 | if not os.path.isdir(CACHE_DIR): 51 | try: 52 | os.makedirs(CACHE_DIR) 53 | except: 54 | import tempdir 55 | 56 | warnings.warn(f"Unable create cache at {CACHE_DIR}. Using temporary directory") 57 | CACHE_DIR = tempdir.gettempdir() 58 | 59 | 60 | try: 61 | from ansys.tools.installer.main import open_gui # place this at end to allow import 62 | except ModuleNotFoundError: # encountered during install 63 | pass 64 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Main entrypoint for this module.""" 24 | 25 | from ansys.tools.installer.main import open_gui 26 | 27 | if __name__ == "__main__": 28 | open_gui() 29 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/ansys-favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/src/ansys/tools/installer/assets/ansys-favicon.ico -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/ansys-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/src/ansys/tools/installer/assets/ansys-favicon.png -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/pyansys-dark-crop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/src/ansys/tools/installer/assets/pyansys-dark-crop.png -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/pyansys-light-crop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/src/ansys/tools/installer/assets/pyansys-light-crop.png -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/pyansys-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/src/ansys/tools/installer/assets/pyansys-light.png -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/pyansys_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansys/python-installer-qt-gui/277f6110c17071e952b7fbc197dd3e84e3a4807c/src/ansys/tools/installer/assets/pyansys_icon.ico -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/pyansys_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/scripts/uninstaller_ubuntu.sh: -------------------------------------------------------------------------------- 1 | echo "Uninstalling Ansys Python Manager......." 2 | sleep 3 3 | 4 | rm -rf ${HOME}/.local/share/icons/pyansys_icon.svg 5 | rm -rf ${HOME}/.local/share/applications/ansys_python_manager.desktop 6 | rm -rf ${HOME}/.local/usr/share/doc/ansys-python-manager 7 | 8 | rm -rf ${HOME}/.local/opt/ansys_python_manager 9 | 10 | sed -i '/# Add alias for Ansys Python Manager/d' ~/.bashrc 11 | sed -i '/alias ansys_python_manager/d' ~/.bashrc -------------------------------------------------------------------------------- /src/ansys/tools/installer/assets/scripts/uninstaller_yum.sh: -------------------------------------------------------------------------------- 1 | while true; do 2 | read -p "Require sudo permission to uninstall this package. Do you want to continue?(Y/N): " user_selection 3 | if [ "$user_selection" = "Y" ] || [ "$user_selection" = "N" ]; then 4 | break 5 | fi 6 | clear 7 | done 8 | printf "\n" 9 | if [ "$user_selection" = "Y" ]; then 10 | #Check sudo 11 | sudo -v >/dev/null 2>&1 12 | root_check=$? 13 | if [ $root_check -eq 0 ]; then 14 | echo "Uninstalling Ansys Python Manager......." 15 | sleep 2 16 | sudo yum remove -y ansys_python_manager.x86_64 17 | sudo sed -i '/# Add alias for Ansys Python Manager/d' ~/.bashrc 18 | sudo sed -i '/alias ansys_python_manager/d' ~/.bashrc 19 | printf "\nUninstalled successfully...\n" 20 | else 21 | echo "You don't have access to sudo. Please try again..." 22 | fi 23 | else 24 | # Script aborted by user 25 | printf "Aborting....\nUser permission denied.... \n\n" 26 | echo "Ansys Python Manager package requires sudo access to uninstall." 27 | fi -------------------------------------------------------------------------------- /src/ansys/tools/installer/auto_updater.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Check for updates.""" 24 | 25 | from github import Github 26 | from packaging import version 27 | 28 | 29 | def query_gh_latest_release(token=None): 30 | """Check GitHub for updates. 31 | 32 | Compares the current version with the version on GitHub. 33 | 34 | Returns the version of the latest release and the download url of 35 | the executable installer. 36 | 37 | Parameters 38 | ---------- 39 | token : str, optional 40 | Token to perform the request. Not necessary, only used on testing 41 | to avoid reaching API request limit. 42 | 43 | Returns 44 | ------- 45 | str 46 | Tag of the latest version. 47 | 48 | str 49 | Url of the latest release installer. 50 | 51 | """ 52 | gh = Github(login_or_token=token) 53 | repo = gh.get_repo(f"ansys/python-installer-qt-gui") 54 | 55 | # Get the latest release and its tag name 56 | latest_release = repo.get_latest_release() 57 | latest_version_tag = latest_release.tag_name 58 | 59 | download_asset = None 60 | for asset in latest_release.get_assets(): 61 | if asset.name.endswith(".exe"): 62 | download_asset = asset 63 | 64 | download_url = None if download_asset is None else download_asset.url 65 | 66 | return version.parse(latest_version_tag), download_url 67 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/common.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Common module for Ansys Python Manager.""" 24 | 25 | from functools import wraps 26 | import json 27 | import logging 28 | import sys 29 | from threading import Thread 30 | import traceback 31 | 32 | import certifi 33 | from pkg_resources import parse_version 34 | import requests 35 | 36 | LOG = logging.getLogger(__name__) 37 | LOG.setLevel("DEBUG") 38 | 39 | 40 | def threaded(fn): 41 | """Call a function using a thread.""" 42 | 43 | def wrapper(*args, **kwargs): 44 | thread = Thread(target=fn, args=args, kwargs=kwargs) 45 | thread.start() 46 | return thread 47 | 48 | return wrapper 49 | 50 | 51 | def protected(fn): 52 | """Capture any exceptions from a function and pass it to the GUI. 53 | 54 | Attempts to display the error using ``show_error`` and protects 55 | the main application from segmentation faulting. 56 | """ 57 | 58 | @wraps(fn) 59 | def wrapper(*args, **kwargs): 60 | self = args[0] 61 | try: 62 | return fn(*args, **kwargs) 63 | except Exception as exception: 64 | exc_info = sys.exc_info() 65 | traceback.print_exception(*exc_info) 66 | LOG.error(exception) 67 | if hasattr(self, "exceptions"): 68 | self._exceptions.append(exception) 69 | 70 | # Visual error handing 71 | if hasattr(self, "parent"): 72 | if hasattr(self.parent, "show_error"): 73 | self.parent.show_error(exception) 74 | return wrapper 75 | 76 | if hasattr(self, "_show_error"): 77 | self._show_error(exception) 78 | 79 | return wrapper 80 | 81 | 82 | def get_pkg_versions(pkg_name): 83 | """ 84 | Get the available versions of a package. 85 | 86 | Parameters 87 | ---------- 88 | pkg_name : str 89 | Name of the package for which to fetch the available versions. 90 | 91 | Returns 92 | ------- 93 | list 94 | A sorted list of available package versions, in descending order. 95 | 96 | Notes 97 | ----- 98 | This function fetches the package information from the PyPI API 99 | and filters the package versions based on specific criteria 100 | for the 'pyansys' package. 101 | 102 | Examples 103 | -------- 104 | >>> get_pkg_versions("numpy") 105 | ['1.22.1', '1.22.0', '1.21.2', ...] 106 | """ 107 | session = requests.Session() 108 | session.verify = False 109 | urls = [ 110 | f"https://pypi.python.org/pypi/{pkg_name}/json", 111 | f"https://pypi.org/pypi/{pkg_name}/json", 112 | ] 113 | all_versions = [""] 114 | 115 | for url in urls: 116 | try: 117 | releases = json.loads(requests.get(url, verify=certifi.where()).content)[ 118 | "releases" 119 | ] 120 | all_versions = sorted(releases, key=parse_version, reverse=True) 121 | if pkg_name == "pyansys": 122 | all_versions = [x for x in all_versions if int(x.split(".")[0]) > 0] 123 | break 124 | except (requests.exceptions.SSLError, requests.exceptions.ConnectionError): 125 | LOG.warning(f"Cannot connect to {url}... No version listed.") 126 | 127 | session.verify = True 128 | 129 | return all_versions 130 | 131 | 132 | def get_targets(pkg_name, version, exclude_tests_and_docs=True): 133 | """ 134 | Get the available targets for a package version. 135 | 136 | Parameters 137 | ---------- 138 | pkg_name : str 139 | Name of the package for which to fetch the available targets. 140 | version : str 141 | Version of the package. 142 | exclude_tests_and_docs : bool, optional 143 | If True, exclude test and documentation targets from the list. 144 | Default is True. 145 | 146 | Returns 147 | ------- 148 | list 149 | A sorted list of available targets for the specified package version. 150 | The first element is an empty string, mimicking the behavior of 151 | no target. 152 | 153 | Examples 154 | -------- 155 | >>> get_targets("pyansys", "0.1.0") 156 | ['target1', 'target2', ...] 157 | """ 158 | session = requests.Session() 159 | session.verify = False 160 | urls = [ 161 | f"https://pypi.python.org/pypi/{pkg_name}/{version}/json", 162 | f"https://pypi.org/pypi/{pkg_name}/{version}/json", 163 | ] 164 | all_targets = [] 165 | 166 | for url in urls: 167 | try: 168 | targets = json.loads(requests.get(url, verify=certifi.where()).content)[ 169 | "info" 170 | ] 171 | # Check if targets are available 172 | if targets.get("provides_extra"): 173 | all_targets = targets["provides_extra"] 174 | break 175 | except (requests.exceptions.SSLError, requests.exceptions.ConnectionError): 176 | LOG.warning(f"Cannot connect to {url}... No target listed.") 177 | 178 | session.verify = True 179 | 180 | # Ensure the first element is an empty string 181 | all_targets.insert(0, "") 182 | 183 | # Exclude test and documentation targets if specified 184 | if exclude_tests_and_docs: 185 | all_targets = [ 186 | target 187 | for target in all_targets 188 | if "tests" not in target.lower() and "doc" not in target.lower() 189 | ] 190 | 191 | return all_targets 192 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/configure.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Configure window.""" 24 | import os 25 | 26 | from PySide6 import QtCore, QtGui, QtWidgets 27 | from PySide6.QtGui import QStandardItem, QStandardItemModel 28 | 29 | from ansys.tools.installer.configure_json import ConfigureJson 30 | from ansys.tools.installer.constants import ( 31 | ANSYS_FAVICON, 32 | VENV_DEFAULT_PATH, 33 | VENV_SEARCH_PATH, 34 | ) 35 | 36 | 37 | class Configure(QtWidgets.QWidget): 38 | """Configure tab.""" 39 | 40 | def __init__(self, parent): 41 | """Initialize this class.""" 42 | try: 43 | super().__init__() 44 | self._parent = parent 45 | self.configure_json = ConfigureJson() 46 | self._parent.configure_window = QtWidgets.QWidget() 47 | self._parent.configure_window.move( 48 | self._parent.configure_window.frameGeometry().center() 49 | ) 50 | configure_window_label = QtWidgets.QLabel() 51 | configure_window_label.setText("Configuration") 52 | configure_window_label.setTextFormat(QtCore.Qt.TextFormat.RichText) 53 | configure_window_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignJustify) 54 | configure_window_label.setWordWrap(True) 55 | 56 | configure_options_layout = QtWidgets.QVBoxLayout() 57 | 58 | # Group 1: Configure default Virtual Environment creation path 59 | configure_window_create_venv = QtWidgets.QGroupBox( 60 | "Default virtual environment creation path:" 61 | ) 62 | configure_window_create_venv_layout = QtWidgets.QVBoxLayout() 63 | configure_window_create_venv_layout.setContentsMargins(10, 20, 10, 20) 64 | configure_window_create_venv.setLayout(configure_window_create_venv_layout) 65 | 66 | # ---> Add box 67 | self.configure_window_create_venv_edit = QtWidgets.QLineEdit() 68 | self.configure_window_create_venv_edit.setText( 69 | self.configure_json.default_path 70 | ) 71 | configure_window_create_venv_layout.addWidget( 72 | self.configure_window_create_venv_edit 73 | ) 74 | 75 | # Finally, add all the previous widgets to the global layout 76 | configure_options_layout.addWidget(configure_window_create_venv) 77 | 78 | # Group 2: Configure Virtual Environment Search path: 79 | configure_window_search_venv = QtWidgets.QGroupBox( 80 | "Locations to search for virtual environments:" 81 | ) 82 | configure_window_search_venv_layout = QtWidgets.QVBoxLayout() 83 | configure_window_search_venv_layout.setContentsMargins(10, 20, 10, 20) 84 | configure_window_search_venv.setLayout(configure_window_search_venv_layout) 85 | 86 | # ---> Add text 87 | self.configure_window_search_venv_combo = QtWidgets.QComboBox() 88 | self.configure_window_search_venv_model = QStandardItemModel() 89 | self._add_elments_to_search_configure() 90 | self.configure_window_search_venv_combo.currentTextChanged.connect( 91 | lambda x: self._change_text_search_venv() 92 | ) 93 | configure_window_search_venv_layout.addWidget( 94 | self.configure_window_search_venv_combo 95 | ) 96 | 97 | # ---> Add box 98 | configure_window_search_venv_Hlayout = QtWidgets.QHBoxLayout() 99 | self.configure_window_search_venv_edit = QtWidgets.QLineEdit() 100 | configure_window_search_venv_Hlayout.addWidget( 101 | self.configure_window_search_venv_edit 102 | ) 103 | 104 | configure_window_search_venv_add = QtWidgets.QPushButton("Add") 105 | configure_window_search_venv_add.clicked.connect( 106 | lambda x: self._add_search_env() 107 | ) 108 | configure_window_search_venv_Hlayout.addWidget( 109 | configure_window_search_venv_add 110 | ) 111 | 112 | configure_window_search_venv_remove = QtWidgets.QPushButton("Remove") 113 | configure_window_search_venv_remove.clicked.connect(self._remove_search_env) 114 | configure_window_search_venv_Hlayout.addWidget( 115 | configure_window_search_venv_remove 116 | ) 117 | 118 | configure_window_search_venv_layout.addLayout( 119 | configure_window_search_venv_Hlayout 120 | ) 121 | 122 | # Finally, add all the previous widgets to the global layout 123 | configure_options_layout.addWidget(configure_window_search_venv) 124 | 125 | configure_window_button_save = QtWidgets.QPushButton("Save") 126 | configure_window_button_save.clicked.connect( 127 | lambda x: self._pop_up("Do you want to save?", self._save_configuration) 128 | ) 129 | configure_window_button_close = QtWidgets.QPushButton("Close") 130 | configure_window_button_close.clicked.connect( 131 | lambda x: self._pop_up("Do you want to close?", self._close_all) 132 | ) 133 | 134 | configure_window_layout_1 = QtWidgets.QHBoxLayout() 135 | configure_window_layout_1.addWidget(configure_window_label) 136 | configure_window_layout_2 = QtWidgets.QHBoxLayout() 137 | configure_window_layout_2.addWidget(configure_window_button_save) 138 | configure_window_layout_2.addWidget(configure_window_button_close) 139 | 140 | configure_window_layout = QtWidgets.QVBoxLayout() 141 | configure_window_layout.addLayout(configure_window_layout_1) 142 | configure_window_layout.addLayout(configure_options_layout) 143 | configure_window_layout.addLayout(configure_window_layout_2) 144 | self._parent.configure_window.setLayout(configure_window_layout) 145 | 146 | self._parent.configure_window.setWindowTitle("Configuration") 147 | self._parent.configure_window.setWindowIcon(QtGui.QIcon(ANSYS_FAVICON)) 148 | self._parent.configure_window.setWindowFlag( 149 | QtCore.Qt.WindowCloseButtonHint, False 150 | ) 151 | self._parent.configure_window.resize(500, 40) 152 | self._parent.configure_window.show() 153 | 154 | except Exception as e: 155 | self._parent.show_error(str(e)) 156 | 157 | def _add_elments_to_search_configure(self): 158 | """Add paths to dropdown based on configure json file.""" 159 | for x in self.configure_json.venv_search_path: 160 | self.configure_window_search_venv_model.appendRow(QStandardItem(x)) 161 | self.configure_window_search_venv_combo.setModel( 162 | self.configure_window_search_venv_model 163 | ) 164 | 165 | def _remove_search_env(self): 166 | """Remove env path to the drop down.""" 167 | if self.configure_window_search_venv_model.rowCount() <= 1: 168 | self._parent.show_error( 169 | "Minimum one path should be available to search virtual environment. Add path to remove the existing one" 170 | ) 171 | elif ( 172 | self.configure_window_search_venv_edit.text() 173 | == self.configure_window_create_venv_edit.text() 174 | ): 175 | self._parent.show_error( 176 | "Cannot delete path which is configured for default virtual environment creation." 177 | ) 178 | else: 179 | i = 0 180 | removed = False 181 | while self.configure_window_search_venv_model.item(i): 182 | if ( 183 | self.configure_window_search_venv_model.item(i).text() 184 | == self.configure_window_search_venv_edit.text() 185 | ): 186 | self.configure_window_search_venv_model.removeRow(i) 187 | self.configure_window_search_venv_combo.setModel( 188 | self.configure_window_search_venv_model 189 | ) 190 | removed = True 191 | i += 1 192 | if not removed: 193 | self._parent.show_error( 194 | "Path is not available in the environment search list." 195 | ) 196 | 197 | def _add_search_env(self): 198 | """Add env path to the drop down.""" 199 | if not os.path.exists(self.configure_window_search_venv_edit.text()): 200 | self._parent.show_error("Path not found. Create path before configure.") 201 | else: 202 | i = 0 203 | while self.configure_window_search_venv_model.item(i): 204 | if ( 205 | self.configure_window_search_venv_model.item(i).text() 206 | == self.configure_window_search_venv_edit.text() 207 | ): 208 | self._parent.show_error( 209 | "Path is already available in the search environment list." 210 | ) 211 | return 212 | i += 1 213 | self.configure_window_search_venv_model.appendRow( 214 | QStandardItem(self.configure_window_search_venv_edit.text()) 215 | ) 216 | self.configure_window_search_venv_combo.setModel( 217 | self.configure_window_search_venv_model 218 | ) 219 | 220 | def _change_text_search_venv(self): 221 | """Change the venv text bsed on the drop down.""" 222 | self.configure_window_search_venv_edit.setText( 223 | self.configure_window_search_venv_combo.currentText() 224 | ) 225 | 226 | def _save_configuration(self): 227 | """Save the configuration.""" 228 | self.configure_json.rewrite_config( 229 | VENV_DEFAULT_PATH, self.configure_window_create_venv_edit.text().strip("\\") 230 | ) 231 | i = 0 232 | venv_search_paths = [] 233 | while self.configure_window_search_venv_model.item(i): 234 | venv_search_paths.append( 235 | self.configure_window_search_venv_model.item(i).text().strip("\\") 236 | ) 237 | i += 1 238 | if ( 239 | self.configure_window_create_venv_edit.text().strip("\\") 240 | not in venv_search_paths 241 | ): 242 | venv_search_paths.append( 243 | self.configure_window_create_venv_edit.text().strip("\\") 244 | ) 245 | self.configure_json.rewrite_config(VENV_SEARCH_PATH, venv_search_paths) 246 | i = 0 247 | 248 | self.configure_json._write_config_file() 249 | self._parent.venv_table_tab.update_table() 250 | 251 | self.user_confirmation_form.close() 252 | self._parent.configure_window.close() 253 | 254 | def _close_all(self): 255 | """Close all the pop-up window.""" 256 | self.user_confirmation_form.close() 257 | self._parent.configure_window.close() 258 | 259 | def _pop_up(self, message, call_back): 260 | """Launch the confirmation pop-up window.""" 261 | self.user_confirmation_form = QtWidgets.QWidget() 262 | self.user_confirmation_form.move( 263 | self.user_confirmation_form.frameGeometry().center() 264 | ) 265 | user_confirmation_label = QtWidgets.QLabel() 266 | user_confirmation_label.setText(message) 267 | user_confirmation_label.setOpenExternalLinks(True) 268 | user_confirmation_label.setTextFormat(QtCore.Qt.TextFormat.RichText) 269 | user_confirmation_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignJustify) 270 | user_confirmation_label.setWordWrap(True) 271 | 272 | user_confirmation_layout_horizontal = QtWidgets.QHBoxLayout() 273 | user_confirmation_yes_button = QtWidgets.QPushButton("Yes") 274 | user_confirmation_yes_button.clicked.connect(call_back) 275 | user_confirmation_no_button = QtWidgets.QPushButton("No") 276 | user_confirmation_no_button.clicked.connect(self.user_confirmation_form.close) 277 | user_confirmation_layout = QtWidgets.QVBoxLayout() 278 | user_confirmation_layout.addWidget(user_confirmation_label) 279 | user_confirmation_layout_horizontal.addWidget(user_confirmation_yes_button) 280 | user_confirmation_layout_horizontal.addWidget(user_confirmation_no_button) 281 | user_confirmation_layout.addLayout(user_confirmation_layout_horizontal) 282 | self.user_confirmation_form.setLayout(user_confirmation_layout) 283 | self.user_confirmation_form.setWindowTitle("Confirmation") 284 | icon = QtGui.QIcon(ANSYS_FAVICON) 285 | self.user_confirmation_form.setWindowIcon(icon) 286 | self.user_confirmation_form.resize(400, 40) 287 | self.user_confirmation_form.setWindowFlag( 288 | QtCore.Qt.WindowCloseButtonHint, False 289 | ) 290 | self.user_confirmation_form.show() 291 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/configure_json.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Configure json file.""" 24 | 25 | import json 26 | import os 27 | 28 | from ansys.tools.installer.constants import ( 29 | ANSYS_VENVS, 30 | VENV_DEFAULT_PATH, 31 | VENV_SEARCH_PATH, 32 | ) 33 | from ansys.tools.installer.linux_functions import ansys_linux_path, is_linux_os 34 | 35 | 36 | class ConfigureJson: 37 | """Configuration json class.""" 38 | 39 | def __init__(self): 40 | """Instantiate Configuration class.""" 41 | self.config_dir = os.path.join( 42 | os.path.expanduser("~"), ".ansys", "ansys_python_manager" 43 | ) 44 | self.config_file_path = os.path.join(self.config_dir, "config.json") 45 | 46 | self.history_file_path = os.path.join(self.config_dir, "history.json") 47 | self.default_path = os.path.join( 48 | ansys_linux_path if is_linux_os() else os.path.expanduser("~"), ANSYS_VENVS 49 | ) 50 | self.venv_search_path = [self.default_path] 51 | self._create_config_file_if_not_exist() 52 | self._read_config_file() 53 | 54 | def _create_config_file_if_not_exist(self): 55 | """Create Configuration file if not exist.""" 56 | if not os.path.exists(self.config_file_path) or ( 57 | not os.path.getsize(self.config_file_path) 58 | ): 59 | os.makedirs(os.path.dirname(self.config_file_path), exist_ok=True) 60 | self.configs = { 61 | "path": { 62 | VENV_DEFAULT_PATH: self.default_path, 63 | VENV_SEARCH_PATH: [self.default_path], 64 | } 65 | } 66 | self._create_history_file_if_not_exist() 67 | self._write_config_file() 68 | 69 | def _create_history_file_if_not_exist(self): 70 | """Create Configuration file if not exist.""" 71 | if not os.path.exists(self.history_file_path) or ( 72 | not os.path.getsize(self.history_file_path) 73 | ): 74 | os.makedirs(os.path.dirname(self.history_file_path), exist_ok=True) 75 | self.history = {"path": [self.default_path]} 76 | else: 77 | self._read_history_file() 78 | 79 | def _read_config_file(self): 80 | """Read configuration file.""" 81 | try: 82 | with open(self.config_file_path) as f: 83 | paths = json.load(f) 84 | self.default_path = paths["path"][VENV_DEFAULT_PATH] 85 | self.venv_search_path = paths["path"][VENV_SEARCH_PATH] 86 | self.configs = paths 87 | except: 88 | self.configs = { 89 | "path": { 90 | VENV_DEFAULT_PATH: self.default_path, 91 | VENV_SEARCH_PATH: [self.default_path], 92 | } 93 | } 94 | self._write_config_file() 95 | 96 | self._read_history_file() 97 | 98 | def _read_history_file(self): 99 | """Read Configuration file.""" 100 | try: 101 | with open(self.history_file_path) as f: 102 | paths = json.load(f) 103 | # Verify it is a dictionary with a key "path" 104 | if "path" not in paths: 105 | raise ValueError("Invalid history file") 106 | self.history = paths 107 | except: 108 | self.history = {"path": [self.default_path]} 109 | self._write_history_file() 110 | 111 | def rewrite_config(self, key, value): 112 | """Rewrite configuration file. 113 | 114 | Parameters 115 | ---------- 116 | key : str 117 | key to save the configuration 118 | value : str 119 | value to save the configuration 120 | """ 121 | if key == VENV_DEFAULT_PATH and value not in self.history["path"]: 122 | self.history["path"].append(value) 123 | self.configs["path"][key] = value 124 | 125 | def _write_config_file(self): 126 | """Write config json file.""" 127 | with open(self.config_file_path, "w+") as f: 128 | f.write(json.dumps(self.configs, indent=4)) 129 | self._write_history_file() 130 | self._read_config_file() 131 | 132 | def _write_history_file(self): 133 | """Write history json file.""" 134 | with open(self.history_file_path, "w+") as f: 135 | f.write(json.dumps(self.history, indent=4)) 136 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Constants file.""" 24 | 25 | import logging 26 | import os 27 | import sys 28 | 29 | from ansys.tools.path.misc import is_linux 30 | 31 | from ansys.tools.installer import __version__ 32 | 33 | LOG = logging.getLogger(__name__) 34 | LOG.setLevel("DEBUG") 35 | 36 | ABOUT_TEXT = f"""

Ansys Python Installer {__version__}

37 |

Created by the PyAnsys Team.

38 |

Project build using PySide6, Copyright 2024 The Qt Company Ltd, and PyInstaller.

39 |

The graphical user interface (GUI) components are licensed under LGPL v3.0.

40 |

Except for the GUI components, your use of this software is governed by the MIT License. In addition, this installer allows you to access and install software that is licensed under separate terms ("Separately Licensed Software"). If you choose to install such Separately Licensed Software, you acknowledge that you are responsible for complying with any associated terms and conditions.

41 |

Copyright 2023 - 2024 ANSYS, Inc. All rights reserved.

42 |

If you have any questions or issues, please open an issue in python-installer-qt-gui Issues page.

43 |

Alternatively, you can contact us at pyansys.core@ansys.com.

44 | """ 45 | 46 | UNABLE_TO_RETRIEVE_LATEST_VERSION_TEXT = f""" 47 |

Ansys Python Installer cannot verify whether it is up-to-date or not. This might be due to a permissions issue.

48 |

Currently installed version is {__version__}.

49 |

To check for the latest released version, visit the latest release site.

50 | """ 51 | 52 | PYANSYS_DOCS_TEXT = f"""

PyAnsys Documentation

53 |

Access the documentation for the different PyAnsys projects by selecting your desired project and clicking on the 'Open Website' button.

54 |

Users are then redirected to the documentation websites for each of the projects.

55 |

Feel free to explore the different PyAnsys initiatives.

56 | """ 57 | 58 | USER_PATH = os.path.expanduser("~") 59 | ANSYS_LINUX_PATH = f".local/ansys" 60 | ANSYS_FULL_LINUX_PATH = f"{USER_PATH}/{ANSYS_LINUX_PATH}" 61 | 62 | ANSYS_VENVS = ".ansys_python_venvs" 63 | 64 | ANSYS_SUPPORTED_PYTHON_VERSIONS = ["3_7", "3_10"] 65 | 66 | INSTALL_TEXT = """Choose to use either the standard Python install from python.org or miniforge.""" 67 | 68 | PYTHON_VERSION_TEXT = """Choose the version of Python to install. 69 | 70 | While choosing the latest version of Python is generally recommended, some third-party libraries and applications may not yet be fully compatible with the newest release. Therefore, it is recommended to try the second newest version, as it will still have most of the latest features and improvements while also having broader support among third-party packages.""" 71 | 72 | PRE_COMPILED_PYTHON_WARNING = """ 73 | NOTE: Only 'Python 3.11' version is readily available. Other Python versions are compiled from source and it takes approximately 2-3 minutes.""" 74 | 75 | PYTHON_VERSION_SELECTION_FOR_VENV = """Choose the version of Python to use for your virtual environment. 76 | 77 | Please select the Python version from the table below to create its respective virtual environment.""" 78 | 79 | NAME_FOR_VENV = f"""Provide the name for your virtual environment. 80 | 81 |

Virtual environments are created under user directory /{ANSYS_LINUX_PATH + "/" + ANSYS_VENVS if is_linux() else ANSYS_VENVS} by default. To configure the default path, go to File >> Configure (Ctrl + D) and provide your preferred path. 82 | 83 | If the name provided already exists for another virtual environment, it will not be created. Users will receive a warning informing of the situation. For more details, refer here.""" 84 | 85 | SELECT_VENV_MANAGE_TAB = f"""Choose a virtual environment to manage. 86 | 87 | It is recommended to use virtual environments for package management and launching options. Environments which are available under the user directory /{ANSYS_LINUX_PATH + "/" + ANSYS_VENVS if is_linux() else ANSYS_VENVS} are listed by default. To configure this default directory, refer here.""" 88 | 89 | if getattr(sys, "frozen", False): 90 | # If the application is run as a bundle, the PyInstaller bootloader 91 | # extends the sys module by a flag frozen=True and sets the app 92 | # path into variable _MEIPASS'. 93 | try: 94 | THIS_PATH = sys._MEIPASS 95 | except: 96 | # this might occur on a single file install 97 | os.path.dirname(sys.executable) 98 | else: 99 | THIS_PATH = os.path.dirname(os.path.abspath(__file__)) 100 | 101 | 102 | ASSETS_PATH = os.path.join(THIS_PATH, "assets") 103 | 104 | ANSYS_FAVICON = os.path.join(ASSETS_PATH, "ansys-favicon.png") 105 | 106 | PYANSYS_DOCS_SITES = { 107 | "PyAnsys": "https://docs.pyansys.com", 108 | "PyAnsys Developer docs": "https://dev.docs.pyansys.com", 109 | "PyACP": "https://acp.docs.pyansys.com", 110 | "PyAdditive": "https://additive.docs.pyansys.com", 111 | "PyAdditive Widgets": "https://widgets.additive.docs.pyansys.com", 112 | "PyAEDT": "https://aedt.docs.pyansys.com", 113 | "PyAnsys Geometry": "https://geometry.docs.pyansys.com", 114 | "PyAnsys Math": "https://math.docs.pyansys.com", 115 | "PyAnsys Sound": "https://sound.docs.pyansys.com", 116 | "PyConceptEV": "https://conceptev.docs.pyansys.com", 117 | "PyDPF - Core": "https://dpf.docs.pyansys.com", 118 | "PyDPF - Post": "https://post.docs.pyansys.com", 119 | "PyDPF - Composites": "https://composites.dpf.docs.pyansys.com", 120 | "PyDyna": "https://dyna.docs.pyansys.com", 121 | "PyDynamicReporting": "https://dynamicreporting.docs.pyansys.com", 122 | "PyEDB": "https://edb.docs.pyansys.com", 123 | "PyEDB - Core": "https://edb.core.docs.pyansys.com", 124 | "PyEnSight": "https://ensight.docs.pyansys.com", 125 | "PyFluent": "https://fluent.docs.pyansys.com", 126 | "PyFluent - Visualization": "https://visualization.fluent.docs.pyansys.com", 127 | "PyGranta": "https://grantami.docs.pyansys.com", 128 | "PyHPS": "https://hps.docs.pyansys.com", 129 | "PyMAPDL": "https://mapdl.docs.pyansys.com", 130 | "PyMAPDL Reader": "https://reader.docs.pyansys.com", 131 | "PyMechanical": "https://mechanical.docs.pyansys.com", 132 | "PyModelCenter": "https://modelcenter.docs.pyansys.com", 133 | "PyMotorCAD": "https://motorcad.docs.pyansys.com", 134 | "PyOptislang": "https://optislang.docs.pyansys.com", 135 | "PyPIM": "https://pypim.docs.pyansys.com", 136 | "PyPrimeMesh": "https://prime.docs.pyansys.com", 137 | "PyRocky": "https://rocky.docs.pyansys.com", 138 | "PySeascape": "https://seascape.docs.pyansys.com", 139 | "PySherlock": "https://sherlock.docs.pyansys.com", 140 | "PySimAI": "https://simai.docs.pyansys.com", 141 | "PySystemCoupling": "https://systemcoupling.docs.pyansys.com", 142 | "PyTurboGrid": "https://turbogrid.docs.pyansys.com", 143 | "PyTwin": "https://twin.docs.pyansys.com", 144 | "PyWorkbench": "https://workbench.docs.pyansys.com", 145 | # TOOLS 146 | "Ansys FileTransfer Tool": "https://filetransfer.tools.docs.pyansys.com", 147 | "Ansys Local Product Launcher": "https://local-product-launcher.tools.docs.pyansys.com", 148 | "Ansys Tools Path": "https://path.tools.docs.pyansys.com", 149 | "Ansys Tools Protobuf Compilation Helper": "https://ansys.github.io/ansys-tools-protoc-helper", 150 | "Ansys Tools Visualization Interface": "https://visualization-interface.tools.docs.pyansys.com", 151 | "PyAnsys Tools Report": "https://report.tools.docs.pyansys.com", 152 | "PyAnsys Tools Variable Interop": "https://variableinterop.docs.pyansys.com", 153 | "PyAnsys Tools Versioning": "https://versioning.tools.docs.pyansys.com", 154 | "PyAnsys Units": "http://units.docs.pyansys.com", 155 | "PyMaterials Manager": "https://manager.materials.docs.pyansys.com", 156 | } 157 | 158 | PYANSYS_LIBS = { 159 | "PyAnsys-Metapackage": "pyansys", 160 | "PyACP": "ansys-acp-core", 161 | "PyAdditive": "ansys-additive-core", 162 | "PyAdditive Widgets": "ansys-additive-widgets", 163 | "PyAEDT": "pyaedt", 164 | "PyAnsys Geometry": "ansys-geometry-core", 165 | "PyAnsys Math": "ansys-math-core", 166 | "PyAnsys Sound": "ansys-sound-core", 167 | "PyConceptEV": "ansys-conceptev-core", 168 | "PyDPF - Core": "ansys-dpf-core", 169 | "PyDPF - Post": "ansys-dpf-post", 170 | "PyDPF - Composites": "ansys-dpf-composites", 171 | "PyDyna": "ansys-dyna-core", 172 | "PyDynamicReporting": "ansys-dynamicreporting-core", 173 | "PyEDB": "pyedb", 174 | "PyEDB - Core": "ansys-edb-core", 175 | "PyEnSight": "ansys-pyensight-core", 176 | "PyFluent": "ansys-fluent-core", 177 | "PyFluent - Visualization": "ansys-fluent-visualization", 178 | "PyGranta": "pygranta", 179 | "PyHPS": "ansys-hps-client", 180 | "PyMAPDL": "ansys-mapdl-core", 181 | "PyMAPDL Reader": "ansys-mapdl-reader", 182 | "PyMechanical": "ansys-mechanical-core", 183 | "PyModelCenter": "ansys-modelcenter-workflow", 184 | "PyMotorCAD": "ansys-motorcad-core", 185 | "PyOptislang": "ansys-optislang-core", 186 | "PyPIM": "ansys-platform-instancemanagement", 187 | "PyPrimeMesh": "ansys-meshing-prime", 188 | "PyRocky": "ansys-rocky-core", 189 | "PySeascape": "ansys-seascape", 190 | "PySherlock": "ansys-sherlock-core", 191 | "PySimAI": "ansys-simai-core", 192 | "PySystemCoupling": "ansys-systemcoupling-core", 193 | "PyTurboGrid": "ansys-turbogrid-core", 194 | "PyTwin": "pytwin", 195 | "PyWorkbench": "ansys-workbench-core", 196 | "Granta MI BoM Analytics": "ansys-grantami-bomanalytics", 197 | "Granta MI RecordLists": "ansys-grantami-recordlists", 198 | "Shared Components": "ansys-openapi-common", 199 | # TOOLS 200 | "Ansys FileTransfer Tool": "ansys-tools-filetransfer", 201 | "Ansys Local Product Launcher": "ansys-tools-local-product-launcher", 202 | "Ansys Tools Path": "ansys-tools-path", 203 | "Ansys Tools Protobuf Compilation Helper": "ansys-tools-protoc-helper", 204 | "Ansys Tools Visualization Interface": "ansys-tools-visualization-interface", 205 | "PyAnsys Tools Report": "pyansys-tools-report", 206 | "PyAnsys Tools Variable Interop": "pyansys-tools-variableinterop", 207 | "PyAnsys Tools Versioning": "pyansys-tools-versioning", 208 | "PyAnsys Units": "ansys-units", 209 | "PyMaterials Manager": "ansys-materials-manager", 210 | } 211 | 212 | VENV_DEFAULT_PATH = "venv_default_path" 213 | VENV_SEARCH_PATH = "venv_search_path" 214 | 215 | 216 | ############################################################################### 217 | # Python versions 218 | ############################################################################### 219 | # 220 | # Do not modify below this section 221 | # 222 | 223 | VANILLA_PYTHON_VERSIONS = { 224 | "Python 3.10": "3.10.11", 225 | "Python 3.11": "3.11.9", 226 | "Python 3.12": "3.12.10", 227 | } 228 | 229 | CONDA_PYTHON_VERSION = "24.1.2-0" 230 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/create_virtual_environment.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Installed Python versions table module for Ansys Python Manager.""" 24 | 25 | import logging 26 | import os 27 | from pathlib import Path 28 | 29 | from PySide6 import QtCore, QtGui, QtWidgets 30 | 31 | from ansys.tools.installer.configure_json import ConfigureJson 32 | from ansys.tools.installer.constants import ( 33 | ANSYS_FAVICON, 34 | NAME_FOR_VENV, 35 | PYTHON_VERSION_SELECTION_FOR_VENV, 36 | ) 37 | from ansys.tools.installer.installed_table import DataTable 38 | from ansys.tools.installer.linux_functions import ( 39 | create_venv_linux, 40 | create_venv_linux_conda, 41 | is_linux_os, 42 | ) 43 | from ansys.tools.installer.windows_functions import ( 44 | create_venv_windows, 45 | create_venv_windows_conda, 46 | ) 47 | 48 | ALLOWED_FOCUS_EVENTS = [QtCore.QEvent.Type.WindowActivate, QtCore.QEvent.Type.Show] 49 | 50 | LOG = logging.getLogger(__name__) 51 | LOG.setLevel("DEBUG") 52 | 53 | 54 | class CreateVenvTab(QtWidgets.QWidget): 55 | """Manage Virtual Environment w.r.t Python versions tab.""" 56 | 57 | def __init__(self, parent): 58 | """Initialize this tab.""" 59 | super().__init__() 60 | self._parent = parent 61 | self.venv_table = parent.installed_table_tab.venv_table 62 | layout = QtWidgets.QVBoxLayout() 63 | self.setLayout(layout) 64 | 65 | # QIcon object from an image file 66 | self.app_icon = QtGui.QIcon(ANSYS_FAVICON) 67 | 68 | # Group 1: Select Python version for virtual environment 69 | python_version_box = QtWidgets.QGroupBox("Select Python version") 70 | python_version_box_layout = QtWidgets.QVBoxLayout() 71 | python_version_box_layout.setContentsMargins(10, 20, 10, 20) 72 | python_version_box.setLayout(python_version_box_layout) 73 | 74 | # ---> Add text for selecting Python version 75 | python_version_box_text = QtWidgets.QLabel() 76 | python_version_box_text.setText(PYTHON_VERSION_SELECTION_FOR_VENV) 77 | python_version_box_text.setAlignment(QtCore.Qt.AlignmentFlag.AlignJustify) 78 | python_version_box_text.setWordWrap(True) 79 | python_version_box_layout.addWidget(python_version_box_text) 80 | 81 | # ---> Include table with available Python installations 82 | self.table = DataTable(installed_python=True, installed_forge=True) 83 | self.table.setSelectionMode(QtWidgets.QTableWidget.SingleSelection) 84 | python_version_box_layout.addWidget(self.table) 85 | 86 | # Group 2: Provide name for virtual environment 87 | venv_name_box = QtWidgets.QGroupBox("Virtual environment name") 88 | venv_name_box_layout = QtWidgets.QVBoxLayout() 89 | venv_name_box_layout.setContentsMargins(10, 20, 10, 20) 90 | venv_name_box.setLayout(venv_name_box_layout) 91 | 92 | # ---> Add text for virtual environment name 93 | venv_name_box_text = QtWidgets.QLabel() 94 | venv_name_box_text.setText(NAME_FOR_VENV) 95 | venv_name_box_text.setTextFormat(QtCore.Qt.TextFormat.RichText) 96 | venv_name_box_text.setAlignment(QtCore.Qt.AlignmentFlag.AlignJustify) 97 | venv_name_box_text.setOpenExternalLinks(True) 98 | venv_name_box_text.setWordWrap(True) 99 | venv_name_box_layout.addWidget(venv_name_box_text) 100 | 101 | # ---> Add box for virtual environment name insertion 102 | self.venv_name = QtWidgets.QLineEdit() 103 | self.venv_name.setPlaceholderText("Enter virtual environment name") 104 | venv_name_box_layout.addWidget(self.venv_name) 105 | 106 | # END: Create virtual environment button 107 | create_env_btn = QtWidgets.QPushButton("Create") 108 | create_env_btn.clicked.connect(self.create_venv) 109 | 110 | # Finally, add all the previous widgets to the global layout 111 | layout.addWidget(python_version_box) 112 | layout.addWidget(venv_name_box) 113 | layout.addWidget(create_env_btn) 114 | 115 | # And ensure the table is always in focus 116 | self.installEventFilter(self) 117 | 118 | def create_venv(self): 119 | """Create virtual environment at selected directory.""" 120 | configure_json = ConfigureJson() 121 | create_venv_path = configure_json.default_path 122 | venv_dir = os.path.join(create_venv_path, self.venv_name.text()) 123 | if self.venv_name.text() == "": 124 | self.failed_to_create_dialog(case_1=True) 125 | elif os.path.exists(venv_dir): 126 | self.failed_to_create_dialog(case_2=True) 127 | else: 128 | Path(venv_dir).mkdir(parents=True, exist_ok=True) 129 | try: 130 | self.cmd_create_venv(venv_dir) 131 | except: 132 | self.failed_to_create_dialog() 133 | 134 | self.update_table() 135 | self.venv_success_dialog() 136 | 137 | def venv_success_dialog(self): 138 | """Dialog appear for successful creation of virtual environment.""" 139 | msg = QtWidgets.QMessageBox() 140 | msg.setText("Information: Virtual environment successfully created!") 141 | msg.setWindowTitle("Information") 142 | msg.setIcon(msg.Icon.Information) 143 | msg.setWindowIcon(self.app_icon) 144 | msg.exec_() 145 | 146 | def failed_to_create_dialog(self, case_1=False, case_2=False): 147 | """Dialogs for if environment gets failed to create.""" 148 | if case_1: 149 | # Case 1: check for name of environment 150 | msg = QtWidgets.QMessageBox() 151 | msg.setText("Warning: Failed to create virtual environment!") 152 | msg.setInformativeText( 153 | "Enter a valid name for virtual environment creation." 154 | ) 155 | msg.setWindowTitle("Warning") 156 | msg.setIcon(msg.Icon.Warning) 157 | msg.setWindowIcon(self.app_icon) 158 | msg.exec_() 159 | 160 | elif case_2: 161 | # Case 2: if environment already exists 162 | msg = QtWidgets.QMessageBox() 163 | msg.setText("Warning: Failed to create virtual environment!") 164 | msg.setInformativeText( 165 | "Virtual environment already exists. Please enter a different virtual environment name." 166 | ) 167 | msg.setWindowTitle("Warning") 168 | msg.setIcon(msg.Icon.Warning) 169 | msg.setWindowIcon(self.app_icon) 170 | msg.exec_() 171 | else: 172 | # In case of critical error 173 | msg = QtWidgets.QMessageBox() 174 | msg.setText("Error: Failed to create virtual environment!") 175 | msg.setInformativeText("There might be some issue with application.") 176 | msg.setWindowTitle("Error") 177 | msg.setIcon(msg.Icon.Critical) 178 | msg.setWindowIcon(self.app_icon) 179 | msg.exec_() 180 | 181 | def update_table(self): 182 | """Update the Python version table.""" 183 | self.table.update() 184 | self.venv_table.update() 185 | 186 | def eventFilter(self, source, event): 187 | """Filter events and ensure that the table always remains in focus.""" 188 | if event.type() in ALLOWED_FOCUS_EVENTS and source is self: 189 | self.table.setFocus() 190 | return super().eventFilter(source, event) 191 | 192 | def cmd_create_venv(self, venv_dir): 193 | """Create a virtual environment in a new command prompt. 194 | 195 | Parameters 196 | ---------- 197 | venv_dir : str 198 | The location for the virtual environment. 199 | """ 200 | # Get the selected Python environment 201 | py_path = self.table.active_path 202 | 203 | LOG.debug(f"Requesting creation of {venv_dir}") 204 | if "Python" in self.table.active_version: 205 | if is_linux_os(): 206 | create_venv_linux(venv_dir, py_path) 207 | else: 208 | create_venv_windows(venv_dir, py_path) 209 | else: 210 | if is_linux_os(): 211 | create_venv_linux_conda(venv_dir, py_path) 212 | else: 213 | create_venv_windows_conda(venv_dir, py_path) 214 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/find_python.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Search for Python or miniforge installations within the Windows registry.""" 24 | 25 | import logging 26 | import os 27 | import subprocess 28 | 29 | from ansys.tools.path import get_available_ansys_installations 30 | 31 | from ansys.tools.installer.configure_json import ConfigureJson 32 | from ansys.tools.installer.constants import ANSYS_SUPPORTED_PYTHON_VERSIONS 33 | from ansys.tools.installer.linux_functions import ( 34 | find_ansys_installed_python_linux, 35 | find_miniforge_linux, 36 | is_linux_os, 37 | ) 38 | 39 | # only used on windows 40 | try: 41 | import winreg 42 | except ModuleNotFoundError: 43 | pass 44 | 45 | 46 | LOG = logging.getLogger(__name__) 47 | LOG.setLevel("DEBUG") 48 | 49 | 50 | def find_miniforge(): 51 | """Find all installations of miniforge within the Windows registry. 52 | 53 | Returns 54 | ------- 55 | dict 56 | Dictionary containing a key for each path and a ``tuple`` 57 | containing ``(version_str, is_admin)``. 58 | 59 | """ 60 | if os.name == "nt": 61 | paths = _find_miniforge_win(True) 62 | paths.update(_find_miniforge_win(False)) 63 | else: 64 | paths = find_miniforge_linux() 65 | return paths 66 | 67 | 68 | def _find_miniforge_win(admin=False): 69 | """Search for any miniforge installations in the registry.""" 70 | if admin: 71 | root_key = winreg.HKEY_LOCAL_MACHINE 72 | else: 73 | root_key = winreg.HKEY_CURRENT_USER 74 | 75 | paths = {} 76 | key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" 77 | try: 78 | with winreg.OpenKey( 79 | root_key, 80 | key, 81 | access=winreg.KEY_READ, 82 | ) as reg_key: 83 | info = winreg.QueryInfoKey(reg_key) 84 | for i in range(info[0]): 85 | subkey_name = winreg.EnumKey(reg_key, i) 86 | if "Miniforge" in subkey_name: 87 | with winreg.OpenKey( 88 | root_key, 89 | key + "\\" + subkey_name, 90 | access=winreg.KEY_READ, 91 | ) as sub_key: 92 | ver = winreg.QueryValueEx(sub_key, "DisplayVersion")[0] 93 | uninstall_exe = winreg.QueryValueEx(sub_key, "UninstallString")[ 94 | 0 95 | ].replace('"', "") 96 | miniforge_path = os.path.dirname(uninstall_exe) 97 | paths[miniforge_path] = (ver, admin) 98 | except FileNotFoundError: 99 | pass 100 | 101 | return paths 102 | 103 | 104 | def _find_installed_python_win(admin=False): 105 | """Check the windows registry for any installed instances of Python.""" 106 | if admin: 107 | root_key = winreg.HKEY_LOCAL_MACHINE 108 | else: 109 | root_key = winreg.HKEY_CURRENT_USER 110 | 111 | paths = {} 112 | try: 113 | base_key = "SOFTWARE\\Python\\PythonCore" 114 | with winreg.OpenKey( 115 | root_key, 116 | base_key, 117 | access=winreg.KEY_READ, 118 | ) as reg_key: 119 | info = winreg.QueryInfoKey(reg_key) 120 | for i in range(info[0]): 121 | name = winreg.EnumKey(reg_key, i) 122 | ver, path = _get_python_info_win(f"{base_key}\\{name}", root_key) 123 | if ver is not None and path is not None: 124 | paths[path] = (ver, admin) 125 | 126 | except FileNotFoundError: 127 | pass 128 | 129 | return paths 130 | 131 | 132 | def _find_installed_ansys_python_win(): 133 | """Check the Ansys installation folder for installed Python.""" 134 | installed_ansys = get_available_ansys_installations() 135 | paths = {} 136 | for ver in installed_ansys: 137 | ansys_path = installed_ansys[ver] 138 | for ansys_py_ver in ANSYS_SUPPORTED_PYTHON_VERSIONS: 139 | path = os.path.join( 140 | ansys_path, 141 | f"commonfiles\\CPython\\{ansys_py_ver}\\winx64\\Release\\python", 142 | ) 143 | if os.path.exists(path): 144 | try: 145 | version_output = subprocess.check_output( 146 | [f"{path}\\python.exe", "--version"], text=True 147 | ).strip() 148 | version = version_output.split()[1] 149 | paths[path] = (version, False) 150 | except Exception as err: 151 | LOG.error(f"Failed to retrieve Python version: {str(err)}") 152 | pass 153 | 154 | return paths 155 | 156 | 157 | def _find_installed_python_linux(): 158 | """ 159 | Find all installed Python versions on Linux. 160 | 161 | Returns 162 | ------- 163 | dict 164 | Dictionary containing a key for each path and a tuple 165 | containing ``(version: str, admin: bool)``. 166 | 167 | Examples 168 | -------- 169 | >>> installed_pythons = find_installed_python_linux() 170 | >>> installed_pythons 171 | {'/home/user/python/py311/bin/python': ('3.11.3', False), 172 | '/home/user/python/py311/bin/python3': ('3.11.3', False), 173 | 174 | """ 175 | LOG.debug("Identifying all installed versions of python on Linux") 176 | 177 | pythons = {} 178 | version_names = ["python", "python3"] + [f"python3.{i}" for i in range(7, 13)] 179 | previous_found_version = "" 180 | 181 | for version_name in version_names: 182 | try: 183 | path = subprocess.check_output(["which", version_name], text=True).strip() 184 | version_output = subprocess.check_output( 185 | [path, "--version"], text=True 186 | ).strip() 187 | version = version_output.split()[1] 188 | admin = path.startswith("/usr") 189 | if version != previous_found_version: 190 | pythons[path] = (version, admin) 191 | LOG.debug("Identified %s at %s", version, path) 192 | previous_found_version = version 193 | except: 194 | # Ignore if the command fails (e.g., if the Python version is not installed) 195 | pass 196 | 197 | return pythons 198 | 199 | 200 | def _get_python_info_win(key, root_key): 201 | """For a given windows key, read the install path and python version.""" 202 | with winreg.OpenKey(root_key, key, access=winreg.KEY_READ) as reg_key: 203 | try: 204 | ver = winreg.QueryValueEx(reg_key, "Version")[0] 205 | except FileNotFoundError: 206 | ver = None 207 | 208 | try: 209 | with winreg.OpenKey( 210 | root_key, f"{key}\\InstallPath", access=winreg.KEY_READ 211 | ) as path_key: 212 | path = winreg.QueryValueEx(path_key, None)[0] 213 | except FileNotFoundError: 214 | path = None 215 | 216 | return ver, path 217 | 218 | 219 | def find_all_python(): 220 | """Find any installed instances of python. 221 | 222 | Returns 223 | ------- 224 | dict 225 | Dictionary containing a key for each path and a ``tuple`` 226 | containing ``(version_str, is_admin)``. 227 | 228 | """ 229 | if os.name == "nt": 230 | paths = _find_installed_python_win(True) 231 | paths.update(_find_installed_python_win(False)) 232 | paths.update(_find_installed_ansys_python_win()) 233 | else: 234 | paths = _find_installed_python_linux() 235 | paths.update(find_ansys_installed_python_linux()) 236 | 237 | return paths 238 | 239 | 240 | def get_all_python_venv(): 241 | """Get a list of all created python virtual environments. 242 | 243 | Returns 244 | ------- 245 | dict 246 | Dictionary containing a key for each path and a ``tuple`` 247 | containing ``(version_str, is_admin)``. 248 | """ 249 | paths = {} 250 | script_path = "bin" if is_linux_os() else "Scripts" 251 | configure = ConfigureJson() 252 | for venv_dir in configure.venv_search_path: 253 | try: 254 | for venv_dir_name in os.listdir(venv_dir): 255 | if os.path.isfile( 256 | os.path.join(venv_dir, venv_dir_name, script_path, "activate") 257 | ) or ( 258 | not os.path.isdir(os.path.join(venv_dir, venv_dir_name, "condabin")) 259 | and os.path.isdir( 260 | os.path.join(venv_dir, venv_dir_name, "conda-meta") 261 | ) 262 | ): 263 | 264 | path = os.path.join(venv_dir, venv_dir_name, script_path) 265 | paths[path] = ( 266 | venv_dir_name, 267 | False, 268 | ) # venvs will always be user-like, hence False 269 | except: 270 | pass 271 | return paths 272 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/installer.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Installer module for Ansys Python Manager.""" 24 | 25 | from ansys.tools.installer.linux_functions import install_python_linux, is_linux_os 26 | from ansys.tools.installer.windows_functions import install_python_windows 27 | 28 | 29 | def install_python(filename, wait=True): 30 | """Install "vanilla" python for a single user.""" 31 | if is_linux_os(): 32 | install_python_linux(filename) 33 | return "Success", None 34 | else: 35 | return install_python_windows(filename, wait) 36 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/misc.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Contains miscellaneous functionalities this library.""" 24 | 25 | import logging 26 | import sys 27 | 28 | from PySide6 import QtCore, QtGui, QtWidgets 29 | 30 | from ansys.tools.installer.constants import ( 31 | ANSYS_FAVICON, 32 | PYANSYS_DOCS_SITES, 33 | PYANSYS_DOCS_TEXT, 34 | ) 35 | 36 | 37 | def enable_logging(): 38 | """Log to stdout.""" 39 | 40 | class SafeStreamHandler(logging.StreamHandler): 41 | def emit(self, record): 42 | try: 43 | if not self.stream.closed: 44 | super().emit(record) 45 | except (ValueError, AttributeError): 46 | pass 47 | 48 | logger = logging.getLogger() 49 | logger.setLevel(logging.DEBUG) 50 | 51 | # Create a console handler that writes to stdout 52 | console_handler = SafeStreamHandler(sys.stdout) 53 | console_handler.setLevel(logging.DEBUG) 54 | formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") 55 | console_handler.setFormatter(formatter) 56 | 57 | # Add the console handler to the logger 58 | logger.addHandler(console_handler) 59 | 60 | 61 | class ImageWidget(QtWidgets.QLabel): 62 | """Automatic scaled image widget.""" 63 | 64 | def __init__(self, parent=None): 65 | """Instantiate an ImageWidget.""" 66 | super().__init__(parent) 67 | self.setScaledContents(True) 68 | 69 | def hasHeightForWidth(self): 70 | """Override height for width for autoscaling (when pixmap).""" 71 | return self.pixmap() is not None 72 | 73 | def heightForWidth(self, w): 74 | """Override height for width for autoscaling.""" 75 | if self.pixmap(): 76 | return int(w * (self.pixmap().height() / self.pixmap().width())) 77 | 78 | 79 | class PyAnsysDocsBox(QtWidgets.QMessageBox): 80 | """PyAnsys documentation message box.""" 81 | 82 | def __init__(self, parent=None): 83 | """Instantiate a PyAnsysDocsBox.""" 84 | super().__init__(parent) 85 | self.setWindowTitle("PyAnsys Documentation") 86 | pixmap = QtGui.QPixmap(ANSYS_FAVICON).scaledToHeight( 87 | 32, QtCore.Qt.SmoothTransformation 88 | ) 89 | self.setWindowIcon(QtGui.QIcon(ANSYS_FAVICON)) 90 | self.setIconPixmap(pixmap) 91 | self.setText(PYANSYS_DOCS_TEXT) 92 | self.setStandardButtons(QtWidgets.QMessageBox.StandardButton.NoButton) 93 | 94 | # create a combo box and add items 95 | self.comboBox = QtWidgets.QComboBox(self) 96 | for key, value in PYANSYS_DOCS_SITES.items(): 97 | self.comboBox.addItem(key, value) 98 | sizePolicy = QtWidgets.QSizePolicy( 99 | QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed 100 | ) 101 | self.comboBox.setSizePolicy(sizePolicy) 102 | 103 | # create a button 104 | self.pushButton = QtWidgets.QPushButton("Open Website", self) 105 | self.pushButton.setSizePolicy(sizePolicy) 106 | 107 | # add the combo box and button to the message box 108 | layout = self.layout() 109 | layout.addWidget(self.comboBox, layout.rowCount(), 0, 1, layout.columnCount()) 110 | layout.addWidget(self.pushButton, layout.rowCount(), 0, 1, layout.columnCount()) 111 | 112 | # add an empty widget to the layout to hold the Close button 113 | spacer = QtWidgets.QWidget(self) 114 | spacer.setSizePolicy(sizePolicy) 115 | layout.addWidget(spacer, layout.rowCount(), 0, 1, layout.columnCount()) 116 | 117 | # create the Close button and add it to the spacer widget 118 | self.closeButton = self.addButton(QtWidgets.QMessageBox.StandardButton.Close) 119 | layout.addWidget( 120 | self.closeButton, layout.rowCount(), 0, 1, layout.columnCount() 121 | ) 122 | 123 | # Hide the close button (for now) 124 | self.closeButton.hide() 125 | 126 | # connect the button to a slot that opens the selected website 127 | self.pushButton.clicked.connect(self.open_website) 128 | 129 | def open_website(self): 130 | """Open the URL to the docs site chosen.""" 131 | # get the selected index 132 | index = self.comboBox.currentIndex() 133 | 134 | # get the URL for the selected index 135 | url = self.comboBox.itemData(index) 136 | 137 | # open the corresponding website 138 | QtGui.QDesktopServices.openUrl(QtCore.QUrl(url)) 139 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/progress_bar.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Progress bar module for Ansys Python Manager.""" 24 | 25 | from PySide6 import QtCore, QtWidgets 26 | 27 | 28 | class ProgressBar(QtWidgets.QDialog): 29 | """ProgressBar class.""" 30 | 31 | def __init__(self, parent, nticks, title="Progress Bar", label=None, show=True): 32 | """Instantiate a ProgressBar object.""" 33 | super().__init__(parent) 34 | self._pbar = QtWidgets.QProgressBar(self) 35 | self._pbar.setValue(0) 36 | # self._pbar.setGeometry(30, 40, 500, 75) 37 | self.layout = QtWidgets.QVBoxLayout() 38 | if label: 39 | label_widget = QtWidgets.QLabel() 40 | label_widget.setText(label) 41 | label_widget.setSizePolicy( 42 | QtWidgets.QSizePolicy.Expanding, 43 | QtWidgets.QSizePolicy.Expanding, 44 | ) 45 | label_widget.setAlignment(QtCore.Qt.AlignCenter) 46 | self.layout.addWidget(label_widget) 47 | self.layout.addWidget(self._pbar) 48 | self.setLayout(self.layout) 49 | self.setGeometry(300, 300, 550, 100) 50 | self.setWindowTitle(title) 51 | if show: 52 | self.show() 53 | 54 | # total number of values for QProgressBar is 100 55 | self._increment_value = 100 // nticks 56 | 57 | def increment(self): 58 | """Increments bar.""" 59 | self._pbar.setValue(self._pbar.value() + self._increment_value) 60 | 61 | def set_value(self, value): 62 | """Set the value of the progress bar.""" 63 | self._pbar.setValue(value) 64 | 65 | @property 66 | def value(self): 67 | """Return the value of the progress bar.""" 68 | return self._pbar.value() 69 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/uninstall.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Uninstall application.""" 24 | import os 25 | import shutil 26 | 27 | from PySide6 import QtCore, QtGui, QtWidgets 28 | 29 | from ansys.tools.installer.configure_json import ConfigureJson 30 | from ansys.tools.installer.constants import ANSYS_FAVICON, ASSETS_PATH 31 | from ansys.tools.installer.linux_functions import ( 32 | execute_linux_command, 33 | find_ansys_installed_python_linux, 34 | find_miniforge_linux, 35 | get_os_version, 36 | is_linux_os, 37 | ) 38 | 39 | 40 | class Uninstall(QtWidgets.QWidget): 41 | """Instantiate uninstall class.""" 42 | 43 | def __init__(self, parent): 44 | """Initialize this tab.""" 45 | try: 46 | super().__init__() 47 | self._parent = parent 48 | self._parent.uninstall_window = QtWidgets.QWidget() 49 | self._parent.uninstall_window.move( 50 | self._parent.uninstall_window.frameGeometry().center() 51 | ) 52 | uninstall_window_label = QtWidgets.QLabel() 53 | uninstall_window_label.setText("Do you want to uninstall the application?") 54 | uninstall_window_label.setTextFormat(QtCore.Qt.TextFormat.RichText) 55 | uninstall_window_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignJustify) 56 | uninstall_window_label.setWordWrap(True) 57 | 58 | uninstall_options_layout = QtWidgets.QVBoxLayout() 59 | 60 | # Group 1: Configure Virtual Environment Create path 61 | uninstall_window_cache_remove = QtWidgets.QGroupBox("Remove:") 62 | uninstall_window_cache_remove_layout = QtWidgets.QVBoxLayout() 63 | uninstall_window_cache_remove_layout.setContentsMargins(10, 20, 10, 20) 64 | uninstall_window_cache_remove.setLayout( 65 | uninstall_window_cache_remove_layout 66 | ) 67 | 68 | # venv 69 | uninstall_window_cache_remove_venv_layout = QtWidgets.QHBoxLayout() 70 | 71 | uninstall_window_cache_remove_venv_text = QtWidgets.QLabel() 72 | uninstall_window_cache_remove_venv_text.setText( 73 | "Delete virtual environments " 74 | ) 75 | uninstall_window_cache_remove_venv_text.setTextFormat( 76 | QtCore.Qt.TextFormat.RichText 77 | ) 78 | uninstall_window_cache_remove_venv_text.setAlignment( 79 | QtCore.Qt.AlignmentFlag.AlignLeft 80 | ) 81 | 82 | self.uninstall_window_cache_remove_venv_checkbox = QtWidgets.QCheckBox() 83 | 84 | uninstall_window_cache_remove_venv_layout.addWidget( 85 | uninstall_window_cache_remove_venv_text 86 | ) 87 | uninstall_window_cache_remove_venv_layout.addWidget( 88 | self.uninstall_window_cache_remove_venv_checkbox 89 | ) 90 | 91 | # remove installed python 92 | uninstall_window_cache_remove_python_layout = QtWidgets.QHBoxLayout() 93 | uninstall_window_cache_remove_python_text = QtWidgets.QLabel() 94 | uninstall_window_cache_remove_python_text.setText( 95 | "Delete Python installations" 96 | ) 97 | uninstall_window_cache_remove_python_text.setTextFormat( 98 | QtCore.Qt.TextFormat.RichText 99 | ) 100 | uninstall_window_cache_remove_python_text.setAlignment( 101 | QtCore.Qt.AlignmentFlag.AlignLeft 102 | ) 103 | 104 | self.uninstall_window_cache_remove_python_checkbox = QtWidgets.QCheckBox() 105 | 106 | uninstall_window_cache_remove_python_layout.addWidget( 107 | uninstall_window_cache_remove_python_text 108 | ) 109 | uninstall_window_cache_remove_python_layout.addWidget( 110 | self.uninstall_window_cache_remove_python_checkbox 111 | ) 112 | 113 | # configs 114 | uninstall_window_cache_remove_configs_layout = QtWidgets.QHBoxLayout() 115 | 116 | uninstall_window_cache_remove_configs_text = QtWidgets.QLabel() 117 | uninstall_window_cache_remove_configs_text.setText("Delete configurations ") 118 | uninstall_window_cache_remove_configs_text.setTextFormat( 119 | QtCore.Qt.TextFormat.RichText 120 | ) 121 | uninstall_window_cache_remove_configs_text.setAlignment( 122 | QtCore.Qt.AlignmentFlag.AlignLeft 123 | ) 124 | uninstall_window_cache_remove_configs_text.setWordWrap(True) 125 | 126 | self.uninstall_window_cache_remove_configs_checkbox = QtWidgets.QCheckBox() 127 | 128 | uninstall_window_cache_remove_configs_layout.addWidget( 129 | uninstall_window_cache_remove_configs_text 130 | ) 131 | uninstall_window_cache_remove_configs_layout.addWidget( 132 | self.uninstall_window_cache_remove_configs_checkbox 133 | ) 134 | 135 | # add layout to group 136 | uninstall_window_cache_remove_layout.addLayout( 137 | uninstall_window_cache_remove_venv_layout 138 | ) 139 | 140 | uninstall_window_cache_remove_layout.addLayout( 141 | uninstall_window_cache_remove_python_layout 142 | ) 143 | 144 | uninstall_window_cache_remove_layout.addLayout( 145 | uninstall_window_cache_remove_configs_layout 146 | ) 147 | 148 | uninstall_options_layout.addWidget(uninstall_window_cache_remove) 149 | 150 | uninstall_window_button_save = QtWidgets.QPushButton("Uninstall") 151 | uninstall_window_button_save.clicked.connect( 152 | lambda x: self._pop_up( 153 | "Do you want to proceed uninstall?", self._uninstall 154 | ) 155 | ) 156 | uninstall_window_button_close = QtWidgets.QPushButton("Close") 157 | uninstall_window_button_close.clicked.connect( 158 | lambda x: self._pop_up("Do you want to close?", self._close_all) 159 | ) 160 | 161 | uninstall_window_layout_2 = QtWidgets.QHBoxLayout() 162 | uninstall_window_layout_2.addWidget(uninstall_window_button_save) 163 | uninstall_window_layout_2.addWidget(uninstall_window_button_close) 164 | 165 | uninstall_window_layout = QtWidgets.QVBoxLayout() 166 | uninstall_window_layout.addLayout(uninstall_options_layout) 167 | uninstall_window_layout.addLayout(uninstall_window_layout_2) 168 | self._parent.uninstall_window.setLayout(uninstall_window_layout) 169 | 170 | self._parent.uninstall_window.setWindowTitle("Uninstall") 171 | self._parent.uninstall_window.setWindowIcon(QtGui.QIcon(ANSYS_FAVICON)) 172 | self._parent.uninstall_window.setWindowFlag( 173 | QtCore.Qt.WindowCloseButtonHint, False 174 | ) 175 | self._parent.uninstall_window.resize(500, 40) 176 | self._parent.uninstall_window.show() 177 | 178 | except Exception as e: 179 | self._parent.show_error(str(e)) 180 | 181 | def _uninstall(self): 182 | """Uninstallation function. Execute the uninstaller script.""" 183 | if self.uninstall_window_cache_remove_venv_checkbox.isChecked(): 184 | self._remove_all_venvs() 185 | 186 | if self.uninstall_window_cache_remove_python_checkbox.isChecked(): 187 | self._remove_all_installed_python() 188 | 189 | if self.uninstall_window_cache_remove_configs_checkbox.isChecked(): 190 | self._remove_configs() 191 | 192 | os_version = get_os_version() 193 | if os_version in ["centos", "fedora"]: 194 | script_path = os.path.join(ASSETS_PATH, "uninstaller_yum.sh") 195 | execute_linux_command(f"{script_path}", wait=False) 196 | elif get_os_version().startswith("2"): 197 | script_path = os.path.join(ASSETS_PATH, "uninstaller_ubuntu.sh") 198 | execute_linux_command(f"{script_path}", wait=False) 199 | 200 | self.user_confirmation_form.close() 201 | self._parent.uninstall_window.close() 202 | self._parent.close_emit() 203 | 204 | def _remove_all_installed_python(self): 205 | for path in find_ansys_installed_python_linux(): 206 | path = path.split("bin")[0] 207 | shutil.rmtree(path, ignore_errors=True) 208 | for path in find_miniforge_linux(ansys_manager_installed_only=True): 209 | shutil.rmtree(path, ignore_errors=True) 210 | 211 | def _remove_all_venvs(self): 212 | """Remove all the venv created by Ansys Python Manager.""" 213 | try: 214 | configure = ConfigureJson() 215 | script_path = "bin" if is_linux_os() else "Scripts" 216 | for venv_dir in configure.history["path"]: 217 | for venv_dir_name in os.listdir(venv_dir): 218 | if os.path.isfile( 219 | os.path.join(venv_dir, venv_dir_name, script_path, "activate") 220 | ) or ( 221 | not os.path.isdir( 222 | os.path.join(venv_dir, venv_dir_name, "condabin") 223 | ) 224 | and os.path.isdir( 225 | os.path.join(venv_dir, venv_dir_name, "conda-meta") 226 | ) 227 | ): 228 | print(f"removed {os.path.join(venv_dir, venv_dir_name)}") 229 | shutil.rmtree( 230 | os.path.join(venv_dir, venv_dir_name), ignore_errors=True 231 | ) 232 | except: 233 | pass 234 | 235 | def _remove_configs(self): 236 | """Remove all the configurations created by Ansys Python Manager.""" 237 | try: 238 | configure = ConfigureJson() 239 | print(f"removed {configure.config_dir}") 240 | shutil.rmtree(configure.config_dir, ignore_errors=True) 241 | except: 242 | pass 243 | 244 | def _close_all(self): 245 | """Close all the pop-up window.""" 246 | self.user_confirmation_form.close() 247 | self._parent.uninstall_window.close() 248 | 249 | def _pop_up(self, message, call_back): 250 | """Launch the confirmation pop-up window.""" 251 | self.user_confirmation_form = QtWidgets.QWidget() 252 | self.user_confirmation_form.move( 253 | self.user_confirmation_form.frameGeometry().center() 254 | ) 255 | user_confirmation_label = QtWidgets.QLabel() 256 | user_confirmation_label.setText(message) 257 | user_confirmation_label.setOpenExternalLinks(True) 258 | user_confirmation_label.setTextFormat(QtCore.Qt.TextFormat.RichText) 259 | user_confirmation_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignJustify) 260 | user_confirmation_label.setWordWrap(True) 261 | 262 | user_confirmation_layout_horizontal = QtWidgets.QHBoxLayout() 263 | user_confirmation_yes_button = QtWidgets.QPushButton("Yes") 264 | user_confirmation_yes_button.clicked.connect(call_back) 265 | user_confirmation_no_button = QtWidgets.QPushButton("No") 266 | user_confirmation_no_button.clicked.connect(self.user_confirmation_form.close) 267 | user_confirmation_layout = QtWidgets.QVBoxLayout() 268 | user_confirmation_layout.addWidget(user_confirmation_label) 269 | user_confirmation_layout_horizontal.addWidget(user_confirmation_yes_button) 270 | user_confirmation_layout_horizontal.addWidget(user_confirmation_no_button) 271 | user_confirmation_layout.addLayout(user_confirmation_layout_horizontal) 272 | self.user_confirmation_form.setLayout(user_confirmation_layout) 273 | self.user_confirmation_form.setWindowTitle("Confirmation") 274 | icon = QtGui.QIcon(ANSYS_FAVICON) 275 | self.user_confirmation_form.setWindowIcon(icon) 276 | self.user_confirmation_form.resize(400, 40) 277 | self.user_confirmation_form.setWindowFlag( 278 | QtCore.Qt.WindowCloseButtonHint, False 279 | ) 280 | self.user_confirmation_form.show() 281 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/vscode.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """VS code launch window.""" 24 | import os 25 | 26 | from PySide6 import QtCore, QtGui, QtWidgets 27 | 28 | from ansys.tools.installer.constants import ANSYS_FAVICON, USER_PATH 29 | 30 | 31 | class VSCode(QtWidgets.QWidget): 32 | """VS code launch window.""" 33 | 34 | def __init__(self, parent): 35 | """Initialize this class.""" 36 | try: 37 | super().__init__() 38 | self._parent = parent 39 | if self.is_vscode_installed(): 40 | self._parent.vscode_window = QtWidgets.QWidget() 41 | self._parent.vscode_window.move( 42 | self._parent.vscode_window.frameGeometry().center() 43 | ) 44 | vscode_window_label = QtWidgets.QLabel() 45 | vscode_window_label.setText("Configuration") 46 | vscode_window_label.setTextFormat(QtCore.Qt.TextFormat.RichText) 47 | vscode_window_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignJustify) 48 | vscode_window_label.setWordWrap(True) 49 | 50 | vscode_layout = QtWidgets.QVBoxLayout() 51 | 52 | # Group 1: Configure default Virtual Environment creation path 53 | vscode_window_path_config = QtWidgets.QGroupBox( 54 | "VS Code open directory:" 55 | ) 56 | vscode_window_path_config_layout = QtWidgets.QVBoxLayout() 57 | vscode_window_path_config_layout.setContentsMargins(10, 20, 10, 20) 58 | vscode_window_path_config.setLayout(vscode_window_path_config_layout) 59 | 60 | # ---> Add box 61 | self.vscode_window_path_config_edit = QtWidgets.QLineEdit() 62 | self.vscode_window_path_config_edit.setText(USER_PATH) 63 | vscode_window_path_config_layout.addWidget( 64 | self.vscode_window_path_config_edit 65 | ) 66 | 67 | self.vscode_warning_text = QtWidgets.QLabel() 68 | self.vscode_warning_text.setAlignment( 69 | QtCore.Qt.AlignmentFlag.AlignJustify 70 | ) 71 | self.vscode_warning_text.setWordWrap(True) 72 | vscode_window_path_config_layout.addWidget(self.vscode_warning_text) 73 | 74 | # Finally, add all the previous widgets to the global layout 75 | vscode_layout.addWidget(vscode_window_path_config) 76 | 77 | vscode_window_button_open = QtWidgets.QPushButton("Open") 78 | vscode_window_button_open.clicked.connect(lambda x: self._open_vscode()) 79 | vscode_window_button_close = QtWidgets.QPushButton("Close") 80 | vscode_window_button_close.clicked.connect(lambda x: self._close_all()) 81 | 82 | vscode_window_layout_1 = QtWidgets.QHBoxLayout() 83 | vscode_window_layout_1.addWidget(vscode_window_label) 84 | vscode_window_layout_2 = QtWidgets.QHBoxLayout() 85 | vscode_window_layout_2.addWidget(vscode_window_button_open) 86 | vscode_window_layout_2.addWidget(vscode_window_button_close) 87 | 88 | vscode_window_layout = QtWidgets.QVBoxLayout() 89 | vscode_window_layout.addLayout(vscode_window_layout_1) 90 | vscode_window_layout.addLayout(vscode_layout) 91 | vscode_window_layout.addLayout(vscode_window_layout_2) 92 | self._parent.vscode_window.setLayout(vscode_window_layout) 93 | 94 | self._parent.vscode_window.setWindowTitle("Configuration") 95 | self._parent.vscode_window.setWindowIcon(QtGui.QIcon(ANSYS_FAVICON)) 96 | self._parent.vscode_window.resize(500, 40) 97 | self._parent.vscode_window.show() 98 | else: 99 | msg = QtWidgets.QMessageBox() 100 | msg.setTextFormat(QtCore.Qt.TextFormat.RichText) 101 | msg.warning( 102 | self, 103 | "VS Code Launch Error", 104 | f"Failed to launch vscode. Try reinstalling code by following this link", 105 | ) 106 | 107 | except Exception as e: 108 | self._parent.show_error(str(e)) 109 | 110 | def _open_vscode(self): 111 | """Open VS code from path.""" 112 | # handle errors 113 | path = self.vscode_window_path_config_edit.text().strip() 114 | if os.path.exists(rf"{path}"): 115 | error_msg = "echo Failed to launch vscode. Try reinstalling code by following this link https://code.visualstudio.com/download" 116 | self._parent.launch_cmd(f'code "{path}" && exit 0 || {error_msg}') 117 | self._parent.vscode_window.close() 118 | else: 119 | self.vscode_warning_text.setText( 120 | f"""{path} does not exist. Provide a valid path.""" 121 | ) 122 | self.vscode_warning_text.setStyleSheet( 123 | """ 124 | color: rgb(255, 0, 0); 125 | """ 126 | ) 127 | 128 | def _close_all(self): 129 | """Close all the pop-up window.""" 130 | self._parent.vscode_window.close() 131 | 132 | def is_vscode_installed(self): 133 | """Check if VSCode is installed or not. 134 | 135 | Returns 136 | ------- 137 | bool 138 | Whether VSCode is installed or not. 139 | """ 140 | try: 141 | return_val = os.system("code --version") 142 | if return_val == 0: 143 | return True 144 | else: 145 | return False 146 | except: 147 | return False 148 | -------------------------------------------------------------------------------- /src/ansys/tools/installer/windows_functions.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | """Windows functions.""" 24 | 25 | import logging 26 | import os 27 | import subprocess 28 | 29 | LOG = logging.getLogger(__name__) 30 | 31 | 32 | def create_venv_windows(venv_dir: str, py_path: str): 33 | r""" 34 | Create a virtual environment on Windows. 35 | 36 | Parameters 37 | ---------- 38 | venv_dir : str 39 | Name of the virtual environment. 40 | py_path : str 41 | Path to the Python executable. 42 | 43 | Examples 44 | -------- 45 | >>> create_venv_windows("my_venv", "C:\\Python39\\python.exe") 46 | 47 | """ 48 | user_profile = os.path.expanduser("~") 49 | 50 | # Update the package managers 51 | try: 52 | # Update pip and uv using the py_path 53 | LOG.debug("Updating package managers - pip & uv...") 54 | subprocess.call( 55 | f'start /w /min cmd /K ""{py_path}\\python.exe" -m pip install --upgrade pip uv && exit"', 56 | shell=True, 57 | cwd=user_profile, 58 | ) 59 | 60 | # Create venv using uv 61 | LOG.debug("Creating virtual environment using uv...") 62 | subprocess.call( 63 | f'start /w /min cmd /K ""{py_path}\\python.exe" -m uv venv --seed {venv_dir} && exit"', 64 | shell=True, 65 | cwd=user_profile, 66 | ) 67 | 68 | # Check & Update default venv packages 69 | LOG.debug("Updating virtual environment packages...") 70 | venv_python = os.path.join(venv_dir, "Scripts", "python.exe") 71 | subprocess.call( 72 | f'start /w /min cmd /K "{venv_python} -m pip install --upgrade pip uv && exit"', 73 | shell=True, 74 | cwd=user_profile, 75 | ) 76 | except Exception as e: 77 | LOG.debug(f"Error creating virtual environment: {e}") 78 | 79 | 80 | def create_venv_windows_conda(venv_dir: str, py_path: str): 81 | r""" 82 | Create a virtual environment on Windows using conda. 83 | 84 | Parameters 85 | ---------- 86 | venv_dir : str 87 | Name of the virtual environment. 88 | py_path : str 89 | Path to the Python executable. 90 | 91 | Examples 92 | -------- 93 | >>> create_venv_windows_conda("my_venv", "C:\\Python39\\python.exe") 94 | 95 | """ 96 | user_profile = os.path.expanduser("~") 97 | subprocess.call( 98 | f'start /w /min cmd /K "{py_path}\\Scripts\\activate.bat && conda create --prefix {venv_dir} python -y && exit"', 99 | shell=True, 100 | cwd=user_profile, 101 | ) 102 | 103 | 104 | def run_ps(command, full_path_to_ps=False): 105 | """Run a powershell command as admin.""" 106 | ps = ( 107 | r"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" 108 | if full_path_to_ps 109 | else "powershell.exe" 110 | ) 111 | ps_command = [ps, command] 112 | 113 | LOG.debug("Running: %s", str(ps_command)) 114 | proc = subprocess.run( 115 | ps_command, 116 | stdout=subprocess.PIPE, 117 | stderr=subprocess.STDOUT, 118 | shell=True, 119 | ) 120 | 121 | # stderr goes through stdout too 122 | if proc.returncode: # If return code != 0 123 | LOG.error("From powershell: %s", proc.stdout) 124 | if full_path_to_ps == False: 125 | return run_ps(command, full_path_to_ps=True) 126 | 127 | else: 128 | LOG.debug("From powershell: %s", proc.stdout) 129 | 130 | return proc.stdout.decode(), proc.returncode 131 | 132 | 133 | def install_python_windows(filename: str, wait: bool) -> tuple[str, int]: 134 | """Install "vanilla" python for a single user. 135 | 136 | Parameters 137 | ---------- 138 | filename : str 139 | Path to the Python installer. 140 | wait : bool 141 | Wait for the installation to complete. 142 | 143 | Returns 144 | ------- 145 | str 146 | Output from the installation. 147 | int 148 | Return code from the installation. 149 | """ 150 | wait_str = " -Wait" if wait else "" 151 | command = f"(Start-Process '{filename}' -ArgumentList '/passive InstallAllUsers=0' {wait_str})" 152 | return run_ps(command) 153 | -------------------------------------------------------------------------------- /tests/test_auto_updater.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import os 24 | 25 | from packaging.version import Version 26 | 27 | from ansys.tools.installer.auto_updater import query_gh_latest_release 28 | 29 | 30 | def test_query_gh_latest_release(): 31 | latest_version_tag, download_url = query_gh_latest_release( 32 | token=os.getenv("GITHUB_TOKEN", None) 33 | ) 34 | 35 | ver = latest_version_tag 36 | assert isinstance(ver, Version) 37 | assert "http" in download_url 38 | -------------------------------------------------------------------------------- /tests/test_gui.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from PySide6 import QtWidgets 24 | import pytest 25 | from pytestqt.qtbot import QtBot 26 | 27 | from ansys.tools.installer.main import AnsysPythonInstaller 28 | 29 | 30 | @pytest.fixture(scope="session") 31 | def qtbot_session(qapp, request): 32 | result = QtBot(qapp) 33 | yield result 34 | 35 | 36 | @pytest.fixture(scope="session") 37 | def gui(qtbot_session): 38 | installer = AnsysPythonInstaller(show=False) 39 | yield installer 40 | # qtbot_session.wait(1000) 41 | # installer.close() 42 | 43 | 44 | def test_main_window_header(gui): 45 | assert isinstance(gui.menu_heading, QtWidgets.QLabel) 46 | 47 | # verify image loaded 48 | pixmap = gui.menu_heading.pixmap() 49 | assert pixmap is not None 50 | assert not pixmap.isNull() 51 | 52 | 53 | def test_downloader(gui): 54 | # this URL is subject to change 55 | url = "https://cdn.jsdelivr.net/gh/belaviyo/download-with/samples/sample.png" 56 | 57 | files = [] 58 | 59 | def when_finished(out_path): 60 | files.append(out_path) 61 | 62 | gui._download(url, "sample.png", when_finished=when_finished) 63 | assert len(files) == 1 64 | assert files[0].endswith("sample.png") 65 | -------------------------------------------------------------------------------- /tests/test_linux_functions.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from ansys.tools.installer.linux_functions import ( 24 | get_conda_url_and_filename, 25 | get_vanilla_url_and_filename, 26 | ) 27 | 28 | 29 | def test_get_vanilla_url_and_filename(): 30 | url, filename = get_vanilla_url_and_filename("3.12.0") 31 | assert url == "https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tar.xz" 32 | assert filename == "Python-3.12.0.tar.xz" 33 | 34 | 35 | def test_get_conda_url_and_filename(): 36 | url, filename = get_conda_url_and_filename("23.1.0-4") 37 | assert ( 38 | url 39 | == "https://github.com/conda-forge/miniforge/releases/download/23.1.0-4/Miniforge3-23.1.0-4-Linux-x86_64.sh" 40 | ) 41 | assert filename == "Miniforge3-23.1.0-4-Linux-x86_64.sh" 42 | -------------------------------------------------------------------------------- /tests/test_metadata.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from ansys.tools.installer import __version__ 24 | 25 | 26 | def test_pkg_version(): 27 | assert isinstance(__version__, str) 28 | -------------------------------------------------------------------------------- /uninstall.nsi: -------------------------------------------------------------------------------- 1 | ; Uninstaller script for Ansys Python Manager 2 | 3 | ; Name and version of the program to be uninstalled are already defined 4 | 5 | Var DeleteDefaultVenvPath 6 | Var DeleteConfiguration 7 | 8 | ; Define the uninstaller section 9 | Section "Uninstall" 10 | ; Prompt the user to confirm uninstallation 11 | MessageBox MB_YESNO|MB_ICONQUESTION "Are you sure you want to uninstall ${PRODUCT_NAME} ${PRODUCT_VERSION}?" IDYES checkDeleteVenvPath 12 | Abort 13 | 14 | checkDeleteVenvPath: 15 | MessageBox MB_YESNO|MB_ICONQUESTION "Do you want to delete the contents in the default virtual environment path location?" IDYES deleteVenvPath 16 | StrCpy $DeleteDefaultVenvPath 0 17 | Goto checkDeleteConfiguration 18 | 19 | deleteVenvPath: 20 | StrCpy $DeleteDefaultVenvPath 1 21 | Goto checkDeleteConfiguration 22 | 23 | checkDeleteConfiguration: 24 | MessageBox MB_YESNO|MB_ICONQUESTION "Do you want to delete the Ansys Python Manager stored configuration?" IDYES deleteConfiguration 25 | StrCpy $DeleteConfiguration 0 26 | Goto doneAsking 27 | 28 | deleteConfiguration: 29 | StrCpy $DeleteConfiguration 1 30 | Goto doneAsking 31 | 32 | doneAsking: 33 | 34 | ; Echo the values of $DeleteConfiguration and $DeleteDefaultVenvPath 35 | DetailPrint "User home: $PROFILE" 36 | DetailPrint "DeleteConfiguration: $DeleteConfiguration" 37 | DetailPrint "DeleteDefaultVenvPath: $DeleteDefaultVenvPath" 38 | 39 | 40 | ; Delete directories if required 41 | ${If} $DeleteDefaultVenvPath == 1 42 | RMDir /r "$PROFILE\.ansys_python_venvs" 43 | ${EndIf} 44 | ${If} $DeleteConfiguration == 1 45 | RMDir /r "$PROFILE\.ansys\ansys_python_manager" 46 | ${EndIf} 47 | 48 | ; Remove the installed files 49 | Delete "$INSTDIR\*.*" 50 | RMDir /r /REBOOTOK "$INSTDIR" 51 | 52 | ; Remove the registry keys 53 | DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" 54 | 55 | ; Remove the start menu shortcut and directory 56 | Delete "$SMPROGRAMS\Ansys Python Manager\Ansys Python Manager.lnk" 57 | RMDir /r /REBOOTOK "$SMPROGRAMS\Ansys Python Manager" 58 | 59 | ; Display the uninstallation complete message 60 | MessageBox MB_OK|MB_ICONINFORMATION "${PRODUCT_NAME} ${PRODUCT_VERSION} has been successfully uninstalled." 61 | SectionEnd 62 | --------------------------------------------------------------------------------