Skip to content

Commit 2031dfb

Browse files
dipinknairpyansys-ci-botpre-commit-ci[bot]
authored
FEAT: Add license_manager (#1118)
Co-authored-by: pyansys-ci-bot <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent f0de6c4 commit 2031dfb

File tree

4 files changed

+253
-0
lines changed

4 files changed

+253
-0
lines changed

doc/changelog.d/1118.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add license_manager

src/ansys/mechanical/core/embedding/app.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from ansys.mechanical.core.embedding.addins import AddinConfiguration
3434
from ansys.mechanical.core.embedding.appdata import UniqueUserProfile
3535
from ansys.mechanical.core.embedding.imports import global_entry_points, global_variables
36+
from ansys.mechanical.core.embedding.license_manager import LicenseManager
3637
from ansys.mechanical.core.embedding.mechanical_warnings import (
3738
connect_warnings,
3839
disconnect_warnings,
@@ -246,6 +247,11 @@ def __init__(self, db_file=None, private_appdata=False, **kwargs):
246247
if globals:
247248
self.update_globals(globals)
248249

250+
# Licensing API is available only for version 2025R2 and later
251+
self._license_manager = None
252+
if self.version >= 252:
253+
self._license_manager = LicenseManager(self)
254+
249255
def __repr__(self):
250256
"""Get the product info."""
251257
import clr
@@ -503,6 +509,13 @@ def messages(self):
503509
self._messages = MessageManager(self._app)
504510
return self._messages
505511

512+
@property
513+
def license_manager(self):
514+
"""Return license manager."""
515+
if self._license_manager is None:
516+
raise Exception("LicenseManager is only available for version 252 and later.")
517+
return self._license_manager
518+
506519
def _share(self, other) -> None:
507520
"""Shares the state of self with other.
508521
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Copyright (C) 2022 - 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+
"""License Manager."""
24+
from typing import List, Optional, Union
25+
26+
from ansys.mechanical.core import LOG
27+
28+
29+
class LicenseManager:
30+
"""Class to manage licenses in Ansys Mechanical.
31+
32+
This class provides methods to enable, disable, and check the status of licenses.
33+
It also allows for moving licenses to specific indices in the license preference list.
34+
It is initialized with an instance of the Ansys Mechanical application.
35+
"""
36+
37+
def __init__(self, app):
38+
"""Initialize the message manager."""
39+
self._app = app
40+
self._license_preference = self._app.ExtAPI.Application.LicensePreference
41+
import Ansys
42+
43+
self._license_status = Ansys.Mechanical.DataModel.Enums.LicenseStatus
44+
45+
def get_all_licenses(self) -> list[str]:
46+
"""Return list of all licenses."""
47+
return self._license_preference.GetAllLicenses()
48+
49+
def get_license_status(
50+
self, license_name: str
51+
) -> "Ansys.Mechanical.DataModel.Enums.LicenseStatus":
52+
"""Return status of the specific license.
53+
54+
Parameters
55+
----------
56+
license_name : str
57+
Name of the license to check.
58+
59+
Returns
60+
-------
61+
"Ansys.Mechanical.DataModel.Enums.LicenseStatus"
62+
The status of the license.
63+
"""
64+
LOG.info(
65+
f"{license_name} status: {self._license_preference.GetLicenseStatus(license_name)}"
66+
)
67+
return self._license_preference.GetLicenseStatus(license_name)
68+
69+
def set_license_status(self, license_name: str, status: bool) -> None:
70+
"""Set the status of a license and save the preference.
71+
72+
Parameters
73+
----------
74+
license_name : str
75+
Name of the license to set the status for.
76+
status : bool
77+
True to enable the license, False to disable it.
78+
"""
79+
if status:
80+
self._license_preference.SetLicenseStatus(license_name, self._license_status.Enabled)
81+
LOG.info(f"{license_name} is enabled.")
82+
else:
83+
self._license_preference.SetLicenseStatus(license_name, self._license_status.Disabled)
84+
LOG.info(f"{license_name} is disabled.")
85+
self._license_preference.Save()
86+
87+
def show(self) -> None:
88+
"""Print all active licenses."""
89+
for lic in self.get_all_licenses():
90+
print(f"{lic} - {self._license_preference.GetLicenseStatus(lic)}")
91+
92+
def disable_session_license(self) -> None:
93+
"""Disable all licenses for current session."""
94+
self._license_preference.DeActivateLicense()
95+
96+
def enable_session_license(self, license: Optional[Union[str, List[str]]] = None) -> None:
97+
"""Enable license(s) for the current session.
98+
99+
Parameters
100+
----------
101+
license : Optional[Union[str, List[str]]], optional
102+
If None, activates the first enabled license in the priority order.
103+
If a string, activates that specific license.
104+
If a list of strings, activates all specified licenses in the order provided.
105+
"""
106+
from System import String
107+
from System.Collections.Generic import List as DotNetList
108+
109+
if license is None:
110+
self._license_preference.ActivateLicense()
111+
elif isinstance(license, str):
112+
self._license_preference.ActivateLicense(String(license))
113+
elif isinstance(license, list):
114+
licenses = DotNetList[String]()
115+
for lic in license:
116+
licenses.Add(String(lic))
117+
self._license_preference.ActivateLicense(licenses)
118+
else:
119+
raise TypeError("License must be None, a string, or a list of strings.")
120+
LOG.info(f"License(s) {license} enabled for the current session.")
121+
122+
def move_to_index(self, license_name: str, location: int) -> None:
123+
"""Move a license preference.
124+
125+
Move license to zero-based index location in the license preference list.
126+
This is useful for setting the preferred license location.
127+
128+
Parameters
129+
----------
130+
license_name : str
131+
License name.
132+
location : int
133+
Location to move the license to.
134+
135+
Examples
136+
--------
137+
Move Ansys Mechanical Premium to the first location.
138+
139+
>>> license_manager = LicenseManager(app)
140+
>>> license_manager.move_to_index('Ansys Mechanical Premium', 0)
141+
"""
142+
LOG.info(f"Moving license preference for {license_name} to location {location}")
143+
self._license_preference.MoveLicenseToLocation(license_name, location)
144+
self._license_preference.Save()
145+
146+
def reset_preference(self) -> None:
147+
"""Reset the license preference.
148+
149+
This method will reset the license location order and the status of all licenses
150+
to the default state.
151+
"""
152+
LOG.info("Resetting license preferences to default state.")
153+
self._license_preference.Reset()
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright (C) 2022 - 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+
"""License manager test"""
24+
25+
import pytest
26+
27+
28+
@pytest.mark.embedding
29+
def test_license_manager(embedded_app, capsys):
30+
"""Test message manager"""
31+
32+
test_license = "Ansys Mechanical Premium"
33+
assert len(embedded_app.license_manager.get_all_licenses()) > 0
34+
assert embedded_app.readonly is False, "App should be editable after enabling session license"
35+
all_licenses = embedded_app.license_manager.get_all_licenses()
36+
assert test_license in all_licenses, "Expected license not found in the list"
37+
38+
# Enable and disable specific license
39+
status = embedded_app.license_manager.get_license_status(test_license)
40+
assert (
41+
status == embedded_app.license_manager._license_status.Enabled
42+
), "License should be enabled"
43+
44+
embedded_app.license_manager.set_license_status(test_license, False)
45+
status = embedded_app.license_manager.get_license_status(test_license)
46+
assert (
47+
status == embedded_app.license_manager._license_status.Disabled
48+
), "License should be disabled"
49+
50+
embedded_app.license_manager.set_license_status(test_license, True)
51+
status = embedded_app.license_manager.get_license_status(test_license)
52+
assert (
53+
status == embedded_app.license_manager._license_status.Enabled
54+
), "License should be enabled"
55+
56+
license_list = embedded_app.license_manager.get_all_licenses()
57+
assert license_list.index(test_license) == 1, "License should be at index 1"
58+
embedded_app.license_manager.move_to_index(test_license, 0)
59+
license_list = embedded_app.license_manager.get_all_licenses()
60+
assert license_list.index(test_license) == 0, "License should be at index 0"
61+
62+
embedded_app.license_manager.reset_preference()
63+
license_list = embedded_app.license_manager.get_all_licenses()
64+
assert license_list.index(test_license) == 1, "License should be at index 1 after reset"
65+
66+
# Enable session license with all cases
67+
embedded_app.license_manager.disable_session_license()
68+
assert embedded_app.readonly is True, "App should be readonly after disabling session license"
69+
embedded_app.license_manager.enable_session_license()
70+
embedded_app.license_manager.disable_session_license()
71+
embedded_app.license_manager.enable_session_license(test_license)
72+
assert embedded_app.readonly is False
73+
embedded_app.license_manager.disable_session_license()
74+
embedded_app.license_manager.enable_session_license(
75+
["Ansys Mechanical Enterprise", test_license]
76+
)
77+
assert embedded_app.readonly is False
78+
79+
with pytest.raises(TypeError):
80+
embedded_app.license_manager.enable_session_license(1)
81+
82+
embedded_app.license_manager.show()
83+
captured = capsys.readouterr()
84+
printed_output = captured.out.strip()
85+
assert "Enabled" in printed_output
86+
assert test_license in printed_output

0 commit comments

Comments
 (0)