Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e2ddca9
add license_manager
dipinknair Mar 7, 2025
cdabb70
chore: adding changelog file 1118.added.md [dependabot-skip]
pyansys-ci-bot Mar 7, 2025
1278c28
Merge branch 'main' into feat/license
dipinknair Mar 10, 2025
28626fc
warning instead of error
dipinknair Mar 10, 2025
0ce00b7
Merge branch 'feat/license' of https://github.com/ansys/pymechanical …
dipinknair Mar 10, 2025
93a2f94
Merge branch 'main' into feat/license
dipinknair Aug 11, 2025
ad59877
chore: adding changelog file 1118.added.md [dependabot-skip]
pyansys-ci-bot Aug 11, 2025
7416c35
Merge branch 'main' into feat/license
dipinknair Aug 12, 2025
334bb08
restore
dipinknair Aug 12, 2025
34ff963
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Aug 12, 2025
b759aa9
update version condition
dipinknair Aug 12, 2025
432780b
Merge branch 'feat/license' of https://github.com/ansys/pymechanical …
dipinknair Aug 12, 2025
f2805c6
Merge branch 'main' into feat/license
dipinknair Aug 14, 2025
bcb7988
update api name
dipinknair Aug 15, 2025
6228b93
Merge branch 'feat/license' of https://github.com/ansys/pymechanical …
dipinknair Aug 15, 2025
3fbd8b7
remove test.py
dipinknair Aug 15, 2025
34c157d
fix reimport
dipinknair Aug 15, 2025
b7ed17a
test to see what all license available
dipinknair Aug 15, 2025
14e5558
add docstring
dipinknair Aug 15, 2025
b2fa716
fix style
dipinknair Aug 15, 2025
35bd07b
show licenses
dipinknair Aug 15, 2025
f897e77
add tests
dipinknair Aug 15, 2025
5ca3e1a
restore test_app
dipinknair Aug 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/1118.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add license_manager
13 changes: 13 additions & 0 deletions src/ansys/mechanical/core/embedding/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from ansys.mechanical.core.embedding.addins import AddinConfiguration
from ansys.mechanical.core.embedding.appdata import UniqueUserProfile
from ansys.mechanical.core.embedding.imports import global_entry_points, global_variables
from ansys.mechanical.core.embedding.license_manager import LicenseManager
from ansys.mechanical.core.embedding.mechanical_warnings import (
connect_warnings,
disconnect_warnings,
Expand Down Expand Up @@ -246,6 +247,11 @@ def __init__(self, db_file=None, private_appdata=False, **kwargs):
if globals:
self.update_globals(globals)

# Licensing API is available only for version 2025R2 and later
self._license_manager = None
if self.version >= 252:
self._license_manager = LicenseManager(self)

def __repr__(self):
"""Get the product info."""
import clr
Expand Down Expand Up @@ -503,6 +509,13 @@ def messages(self):
self._messages = MessageManager(self._app)
return self._messages

@property
def license_manager(self):
"""Return license manager."""
if self._license_manager is None:
raise Exception("LicenseManager is only available for version 252 and later.")
return self._license_manager

def _share(self, other) -> None:
"""Shares the state of self with other.

Expand Down
153 changes: 153 additions & 0 deletions src/ansys/mechanical/core/embedding/license_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""License Manager."""
from typing import List, Optional, Union

from ansys.mechanical.core import LOG


class LicenseManager:
"""Class to manage licenses in Ansys Mechanical.

This class provides methods to enable, disable, and check the status of licenses.
It also allows for moving licenses to specific indices in the license preference list.
It is initialized with an instance of the Ansys Mechanical application.
"""

def __init__(self, app):
"""Initialize the message manager."""
self._app = app
self._license_preference = self._app.ExtAPI.Application.LicensePreference
import Ansys

self._license_status = Ansys.Mechanical.DataModel.Enums.LicenseStatus

def get_all_licenses(self) -> list[str]:
"""Return list of all licenses."""
return self._license_preference.GetAllLicenses()

def get_license_status(
self, license_name: str
) -> "Ansys.Mechanical.DataModel.Enums.LicenseStatus":
"""Return status of the specific license.

Parameters
----------
license_name : str
Name of the license to check.

Returns
-------
"Ansys.Mechanical.DataModel.Enums.LicenseStatus"
The status of the license.
"""
LOG.info(
f"{license_name} status: {self._license_preference.GetLicenseStatus(license_name)}"
)
return self._license_preference.GetLicenseStatus(license_name)

def set_license_status(self, license_name: str, status: bool) -> None:
"""Set the status of a license and save the preference.

Parameters
----------
license_name : str
Name of the license to set the status for.
status : bool
True to enable the license, False to disable it.
"""
if status:
self._license_preference.SetLicenseStatus(license_name, self._license_status.Enabled)
LOG.info(f"{license_name} is enabled.")
else:
self._license_preference.SetLicenseStatus(license_name, self._license_status.Disabled)
LOG.info(f"{license_name} is disabled.")
self._license_preference.Save()

def show(self) -> None:
"""Print all active licenses."""
for lic in self.get_all_licenses():
print(f"{lic} - {self._license_preference.GetLicenseStatus(lic)}")

def disable_session_license(self) -> None:
"""Disable all licenses for current session."""
self._license_preference.DeActivateLicense()

def enable_session_license(self, license: Optional[Union[str, List[str]]] = None) -> None:
"""Enable license(s) for the current session.

Parameters
----------
license : Optional[Union[str, List[str]]], optional
If None, activates the first enabled license in the priority order.
If a string, activates that specific license.
If a list of strings, activates all specified licenses in the order provided.
"""
from System import String
from System.Collections.Generic import List as DotNetList

if license is None:
self._license_preference.ActivateLicense()
elif isinstance(license, str):
self._license_preference.ActivateLicense(String(license))
elif isinstance(license, list):
licenses = DotNetList[String]()
for lic in license:
licenses.Add(String(lic))
self._license_preference.ActivateLicense(licenses)
else:
raise TypeError("License must be None, a string, or a list of strings.")
LOG.info(f"License(s) {license} enabled for the current session.")

def move_to_index(self, license_name: str, location: int) -> None:
"""Move a license preference.

Move license to zero-based index location in the license preference list.
This is useful for setting the preferred license location.

Parameters
----------
license_name : str
License name.
location : int
Location to move the license to.

Examples
--------
Move Ansys Mechanical Premium to the first location.

>>> license_manager = LicenseManager(app)
>>> license_manager.move_to_index('Ansys Mechanical Premium', 0)
"""
LOG.info(f"Moving license preference for {license_name} to location {location}")
self._license_preference.MoveLicenseToLocation(license_name, location)
self._license_preference.Save()

def reset_preference(self) -> None:
"""Reset the license preference.

This method will reset the license location order and the status of all licenses
to the default state.
"""
LOG.info("Resetting license preferences to default state.")
self._license_preference.Reset()
86 changes: 86 additions & 0 deletions tests/embedding/test_license_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""License manager test"""

import pytest


@pytest.mark.embedding
def test_license_manager(embedded_app, capsys):
"""Test message manager"""

test_license = "Ansys Mechanical Premium"
assert len(embedded_app.license_manager.get_all_licenses()) > 0
assert embedded_app.readonly is False, "App should be editable after enabling session license"
all_licenses = embedded_app.license_manager.get_all_licenses()
assert test_license in all_licenses, "Expected license not found in the list"

# Enable and disable specific license
status = embedded_app.license_manager.get_license_status(test_license)
assert (
status == embedded_app.license_manager._license_status.Enabled
), "License should be enabled"

embedded_app.license_manager.set_license_status(test_license, False)
status = embedded_app.license_manager.get_license_status(test_license)
assert (
status == embedded_app.license_manager._license_status.Disabled
), "License should be disabled"

embedded_app.license_manager.set_license_status(test_license, True)
status = embedded_app.license_manager.get_license_status(test_license)
assert (
status == embedded_app.license_manager._license_status.Enabled
), "License should be enabled"

license_list = embedded_app.license_manager.get_all_licenses()
assert license_list.index(test_license) == 1, "License should be at index 1"
embedded_app.license_manager.move_to_index(test_license, 0)
license_list = embedded_app.license_manager.get_all_licenses()
assert license_list.index(test_license) == 0, "License should be at index 0"

embedded_app.license_manager.reset_preference()
license_list = embedded_app.license_manager.get_all_licenses()
assert license_list.index(test_license) == 1, "License should be at index 1 after reset"

# Enable session license with all cases
embedded_app.license_manager.disable_session_license()
assert embedded_app.readonly is True, "App should be readonly after disabling session license"
embedded_app.license_manager.enable_session_license()
embedded_app.license_manager.disable_session_license()
embedded_app.license_manager.enable_session_license(test_license)
assert embedded_app.readonly is False
embedded_app.license_manager.disable_session_license()
embedded_app.license_manager.enable_session_license(
["Ansys Mechanical Enterprise", test_license]
)
assert embedded_app.readonly is False

with pytest.raises(TypeError):
embedded_app.license_manager.enable_session_license(1)

embedded_app.license_manager.show()
captured = capsys.readouterr()
printed_output = captured.out.strip()
assert "Enabled" in printed_output
assert test_license in printed_output
Loading