python4cpm 1.0.22__tar.gz → 1.0.23__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python4cpm
3
- Version: 1.0.22
3
+ Version: 1.0.23
4
4
  Summary: Python for CPM
5
5
  Author-email: Gonzalo Atienza Rela <gonatienza@gmail.com>
6
- License: MIT License
7
-
8
- Copyright (c) 2026 Gonzalo Atienza Rela
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in
18
- all copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
- THE SOFTWARE.
27
-
6
+ License-Expression: MIT
28
7
  Requires-Python: >=3.10
29
8
  Description-Content-Type: text/markdown
30
9
  License-File: LICENSE
@@ -32,7 +11,7 @@ Dynamic: license-file
32
11
 
33
12
  # Python4CPM
34
13
 
35
- A simple way of using python scripts with CyberArk CPM rotations. This module leverages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
14
+ A simple way of using python scripts with CyberArk CPM/SRS rotations. This module leverages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
36
15
 
37
16
  This platform allows you to duplicate it multiple times, simply changing its settings from Privilege Cloud/PVWA to point to different python scripts leveraging the module `python4cpm`.
38
17
 
@@ -40,14 +19,14 @@ This platform allows you to duplicate it multiple times, simply changing its set
40
19
 
41
20
  ### Preparing Python
42
21
 
43
- 1. Install Python in CPM.
22
+ 1. Install Python along CPM or the SRS Connector Management Agent.
44
23
  - **Python must be installed for all users**. Follow the custom install steps from the installation wizard to check the checkbox.
45
- 3. Create a venv in CPM, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
24
+ 3. Create a venv in the server, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
46
25
  4. Install `python4cpm` in your venv:
47
26
  - If your CPM can connect to the internet, install with `c:\venv\Scripts\pip install python4cpm`.
48
27
  - If your CPM cannot connect to the internet:
49
28
  - Download the latest `python4cpm-*.whl` file from the [pypi project files](https://pypi.org/project/python4cpm/#files).
50
- - Copy the file to CPM and extract to a temporary directory called `python4cpm-wheel`.
29
+ - Copy the file to the server into a temporary directory called `python4cpm-wheel`.
51
30
  - From the parent directory of `python4cpm-wheel` run `c:\venv\Scripts\pip install --no-index --find-links=.\python4cpm-wheel python4cpm`.
52
31
 
53
32
 
@@ -84,7 +63,7 @@ This platform allows you to duplicate it multiple times, simply changing its set
84
63
  from python4cpm import Python4CPMHandler
85
64
 
86
65
 
87
- class MyApp(Python4CPMHandler): # create a subclass for the Handler
66
+ class MyRotator(Python4CPMHandler): # create a subclass for the Handler
88
67
  """
89
68
  These are the usable properties and methods from Python4CPMHandler:
90
69
 
@@ -101,13 +80,13 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
101
80
 
102
81
  Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
103
82
 
104
- self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyApp.log
105
- self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyApp.log
106
- self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyApp.log
83
+ self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyRotator.log
84
+ self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyRotator.log
85
+ self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyRotator.log
107
86
 
108
87
  Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
109
88
 
110
- self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyApp.log if logging level is set to debug
89
+ self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyRotator.log if logging level is set to debug
111
90
 
112
91
  =============================
113
92
  REQUIRED TERMINATION SIGNALS
@@ -160,9 +139,9 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
160
139
  # for your logic in a verification
161
140
  result = True
162
141
  if result is True:
163
- self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyApp.log
142
+ self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyRotator.log
164
143
  else:
165
- self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyApp.log
144
+ self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
166
145
  self.close_fail()
167
146
 
168
147
  def _change(self, from_reconcile=False):
@@ -176,26 +155,26 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
176
155
  # self.secrets.reconcile_password.get() and self.secrets.new_password.get() for your logic in a reconciliation
177
156
  result = True
178
157
  if result is True:
179
- self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyApp.log
158
+ self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyRotator.log
180
159
  else:
181
- self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyApp.log
160
+ self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
182
161
  self.close_fail()
183
162
 
184
163
 
185
164
  if __name__ == "__main__":
186
- MyApp().run() # initializes the class and calls the action that was requested from CPM/SRS.
165
+ MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
187
166
  ```
188
167
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpmhandler).
189
168
 
190
169
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
191
- 1. Verify -> the sciprt will be executed once with the `MyApp.verify()` method.
192
- 2. Change -> the sciprt will be executed twice, once with the action `MyApp.logon()` method and once as `MyApp.change()` method.
193
- - If all actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
194
- 3. Reconcile -> the sciprt will be executed twice, once with the action `MyApp.prereconcile()` method and once as `MyApp.reconcile()` method.
195
- - If all actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
196
- 4. When calling `MyApp.logon()` or `MyApp.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
197
- 5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return an empty string.
198
- 6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return an empty string.
170
+ 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
171
+ 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
172
+ - If both actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
173
+ 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
174
+ - If both actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
175
+ 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
176
+ 5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return empty strings.
177
+ 6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return empty strings.
199
178
 
200
179
 
201
180
  ### Using Python4CPM properties and methods directly (for low level controls):
@@ -299,12 +278,12 @@ if __name__ == "__main__":
299
278
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
300
279
  1. Verify -> the sciprt will be executed once with the `p4cpm.args.action` as `Python4CPM.ACTION_VERIFY`.
301
280
  2. Change -> the sciprt will be executed twice, once with the action `p4cpm.args.action` as `Python4CPM.ACTION_LOGON` and once as `Python4CPM.ACTION_CHANGE`.
302
- - If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
281
+ - If both actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
303
282
  3. Reconcile -> the sciprt will be executed twice, once with the `p4cpm.args.action` as `Python4CPM.ACTION_PRERECONCILE` and once as `Python4CPM.ACTION_RECONCILE`.
304
- - If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
283
+ - If both actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
305
284
  4. When `p4cpm.args.action` comes as `Python4CPM.ACTION_VERIFY`, `Python4CPM.ACTION_LOGON` or `Python4CPM.ACTION_PRERECONCILE`: `p4cpm.secrets.new_password.get()` will always return an empty string.
306
- 5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return an empty string.
307
- 6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return an empty string.
285
+ 5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return empty strings.
286
+ 6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return empty strings.
308
287
 
309
288
 
310
289
  ### Installing dependencies in python venv
@@ -329,6 +308,8 @@ pip install python4cpm
329
308
 
330
309
  ### Example:
331
310
 
311
+ #### Set your arguments and secrets:
312
+
332
313
  ```python
333
314
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
334
315
  from getpass import getpass
@@ -339,11 +320,7 @@ password = getpass("password: ") # password from account
339
320
  logon_password = getpass("logon_password: ") # password from linked logon account
340
321
  reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
341
322
  new_password = getpass("new_password: ") # new password for the rotation
342
- ```
343
-
344
- #### Set your arguments and secrets:
345
323
 
346
- ```python
347
324
  NETHelper.set(
348
325
  action=Python4CPM.ACTION_LOGON, # use actions from Python4CPM.ACTION_*
349
326
  address="myapp.corp.local", # populate with the address from your account properties
@@ -362,7 +339,7 @@ NETHelper.set(
362
339
  #### Using the handler (recommended):
363
340
 
364
341
  ```python
365
- class MyApp(Python4CPMHandler):
342
+ class MyRotator(Python4CPMHandler):
366
343
  def verify(self):
367
344
  # TODO: Add your logic here
368
345
  self.close_success()
@@ -383,7 +360,7 @@ class MyApp(Python4CPMHandler):
383
360
  # TODO: Add your logic here
384
361
  self.close_success()
385
362
 
386
- MyApp().run()
363
+ MyRotator().run()
387
364
  ```
388
365
 
389
366
  #### Using Python4CPM properties and methods directly:
@@ -399,6 +376,7 @@ p4cpm.close_success()
399
376
 
400
377
  #### Remember for your final script:
401
378
 
402
- - Change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")` if you are using the properties and methods directly.
403
- - Remove any secrets prompting or interactive interruptions.
404
379
  - Remove the import of `NETHelper`.
380
+ - Remove the `NETHelper.set()` call.
381
+ - If applicable, change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")`.
382
+ - Remove any secrets prompting or interactive interruptions.
@@ -1,6 +1,6 @@
1
1
  # Python4CPM
2
2
 
3
- A simple way of using python scripts with CyberArk CPM rotations. This module leverages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
3
+ A simple way of using python scripts with CyberArk CPM/SRS rotations. This module leverages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
4
4
 
5
5
  This platform allows you to duplicate it multiple times, simply changing its settings from Privilege Cloud/PVWA to point to different python scripts leveraging the module `python4cpm`.
6
6
 
@@ -8,14 +8,14 @@ This platform allows you to duplicate it multiple times, simply changing its set
8
8
 
9
9
  ### Preparing Python
10
10
 
11
- 1. Install Python in CPM.
11
+ 1. Install Python along CPM or the SRS Connector Management Agent.
12
12
  - **Python must be installed for all users**. Follow the custom install steps from the installation wizard to check the checkbox.
13
- 3. Create a venv in CPM, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
13
+ 3. Create a venv in the server, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
14
14
  4. Install `python4cpm` in your venv:
15
15
  - If your CPM can connect to the internet, install with `c:\venv\Scripts\pip install python4cpm`.
16
16
  - If your CPM cannot connect to the internet:
17
17
  - Download the latest `python4cpm-*.whl` file from the [pypi project files](https://pypi.org/project/python4cpm/#files).
18
- - Copy the file to CPM and extract to a temporary directory called `python4cpm-wheel`.
18
+ - Copy the file to the server into a temporary directory called `python4cpm-wheel`.
19
19
  - From the parent directory of `python4cpm-wheel` run `c:\venv\Scripts\pip install --no-index --find-links=.\python4cpm-wheel python4cpm`.
20
20
 
21
21
 
@@ -52,7 +52,7 @@ This platform allows you to duplicate it multiple times, simply changing its set
52
52
  from python4cpm import Python4CPMHandler
53
53
 
54
54
 
55
- class MyApp(Python4CPMHandler): # create a subclass for the Handler
55
+ class MyRotator(Python4CPMHandler): # create a subclass for the Handler
56
56
  """
57
57
  These are the usable properties and methods from Python4CPMHandler:
58
58
 
@@ -69,13 +69,13 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
69
69
 
70
70
  Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
71
71
 
72
- self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyApp.log
73
- self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyApp.log
74
- self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyApp.log
72
+ self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyRotator.log
73
+ self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyRotator.log
74
+ self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyRotator.log
75
75
 
76
76
  Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
77
77
 
78
- self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyApp.log if logging level is set to debug
78
+ self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyRotator.log if logging level is set to debug
79
79
 
80
80
  =============================
81
81
  REQUIRED TERMINATION SIGNALS
@@ -128,9 +128,9 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
128
128
  # for your logic in a verification
129
129
  result = True
130
130
  if result is True:
131
- self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyApp.log
131
+ self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyRotator.log
132
132
  else:
133
- self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyApp.log
133
+ self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
134
134
  self.close_fail()
135
135
 
136
136
  def _change(self, from_reconcile=False):
@@ -144,26 +144,26 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
144
144
  # self.secrets.reconcile_password.get() and self.secrets.new_password.get() for your logic in a reconciliation
145
145
  result = True
146
146
  if result is True:
147
- self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyApp.log
147
+ self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyRotator.log
148
148
  else:
149
- self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyApp.log
149
+ self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
150
150
  self.close_fail()
151
151
 
152
152
 
153
153
  if __name__ == "__main__":
154
- MyApp().run() # initializes the class and calls the action that was requested from CPM/SRS.
154
+ MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
155
155
  ```
156
156
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpmhandler).
157
157
 
158
158
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
159
- 1. Verify -> the sciprt will be executed once with the `MyApp.verify()` method.
160
- 2. Change -> the sciprt will be executed twice, once with the action `MyApp.logon()` method and once as `MyApp.change()` method.
161
- - If all actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
162
- 3. Reconcile -> the sciprt will be executed twice, once with the action `MyApp.prereconcile()` method and once as `MyApp.reconcile()` method.
163
- - If all actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
164
- 4. When calling `MyApp.logon()` or `MyApp.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
165
- 5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return an empty string.
166
- 6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return an empty string.
159
+ 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
160
+ 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
161
+ - If both actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
162
+ 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
163
+ - If both actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
164
+ 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
165
+ 5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return empty strings.
166
+ 6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return empty strings.
167
167
 
168
168
 
169
169
  ### Using Python4CPM properties and methods directly (for low level controls):
@@ -267,12 +267,12 @@ if __name__ == "__main__":
267
267
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
268
268
  1. Verify -> the sciprt will be executed once with the `p4cpm.args.action` as `Python4CPM.ACTION_VERIFY`.
269
269
  2. Change -> the sciprt will be executed twice, once with the action `p4cpm.args.action` as `Python4CPM.ACTION_LOGON` and once as `Python4CPM.ACTION_CHANGE`.
270
- - If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
270
+ - If both actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
271
271
  3. Reconcile -> the sciprt will be executed twice, once with the `p4cpm.args.action` as `Python4CPM.ACTION_PRERECONCILE` and once as `Python4CPM.ACTION_RECONCILE`.
272
- - If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
272
+ - If both actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
273
273
  4. When `p4cpm.args.action` comes as `Python4CPM.ACTION_VERIFY`, `Python4CPM.ACTION_LOGON` or `Python4CPM.ACTION_PRERECONCILE`: `p4cpm.secrets.new_password.get()` will always return an empty string.
274
- 5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return an empty string.
275
- 6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return an empty string.
274
+ 5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return empty strings.
275
+ 6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return empty strings.
276
276
 
277
277
 
278
278
  ### Installing dependencies in python venv
@@ -297,6 +297,8 @@ pip install python4cpm
297
297
 
298
298
  ### Example:
299
299
 
300
+ #### Set your arguments and secrets:
301
+
300
302
  ```python
301
303
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
302
304
  from getpass import getpass
@@ -307,11 +309,7 @@ password = getpass("password: ") # password from account
307
309
  logon_password = getpass("logon_password: ") # password from linked logon account
308
310
  reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
309
311
  new_password = getpass("new_password: ") # new password for the rotation
310
- ```
311
-
312
- #### Set your arguments and secrets:
313
312
 
314
- ```python
315
313
  NETHelper.set(
316
314
  action=Python4CPM.ACTION_LOGON, # use actions from Python4CPM.ACTION_*
317
315
  address="myapp.corp.local", # populate with the address from your account properties
@@ -330,7 +328,7 @@ NETHelper.set(
330
328
  #### Using the handler (recommended):
331
329
 
332
330
  ```python
333
- class MyApp(Python4CPMHandler):
331
+ class MyRotator(Python4CPMHandler):
334
332
  def verify(self):
335
333
  # TODO: Add your logic here
336
334
  self.close_success()
@@ -351,7 +349,7 @@ class MyApp(Python4CPMHandler):
351
349
  # TODO: Add your logic here
352
350
  self.close_success()
353
351
 
354
- MyApp().run()
352
+ MyRotator().run()
355
353
  ```
356
354
 
357
355
  #### Using Python4CPM properties and methods directly:
@@ -367,6 +365,7 @@ p4cpm.close_success()
367
365
 
368
366
  #### Remember for your final script:
369
367
 
370
- - Change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")` if you are using the properties and methods directly.
371
- - Remove any secrets prompting or interactive interruptions.
372
368
  - Remove the import of `NETHelper`.
369
+ - Remove the `NETHelper.set()` call.
370
+ - If applicable, change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")`.
371
+ - Remove any secrets prompting or interactive interruptions.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python4cpm"
7
- version = "1.0.22"
7
+ version = "1.0.23"
8
8
  description = "Python for CPM"
9
9
  authors = [
10
10
  { name = "Gonzalo Atienza Rela", email = "gonatienza@gmail.com" }
@@ -12,7 +12,8 @@ authors = [
12
12
  dependencies = []
13
13
  requires-python = ">=3.10"
14
14
  readme = { file = "README.md", content-type = "text/markdown" }
15
- license = { file = "LICENSE" }
15
+ license = "MIT"
16
+ license-files = ["LICENSE"]
16
17
 
17
18
  [tool.setuptools]
18
19
  package-dir = { "" = "src" }
@@ -0,0 +1,44 @@
1
+ import os
2
+ import logging
3
+ from logging.handlers import RotatingFileHandler
4
+
5
+
6
+ class Logger:
7
+ _LOGS_DIR = os.path.join("Logs", "ThirdParty")
8
+ _LOGGING_ENABLED_VALUE = "yes"
9
+ _LOGGING_LEVELS = {
10
+ "info": logging.INFO,
11
+ "debug": logging.DEBUG
12
+ }
13
+
14
+ @classmethod
15
+ def get_logger(
16
+ cls,
17
+ name: str,
18
+ args_logging: str,
19
+ args_logging_level: str
20
+ ) -> logging.Logger:
21
+ if args_logging.lower() != cls._LOGGING_ENABLED_VALUE:
22
+ return None
23
+ os.makedirs(cls._LOGS_DIR, exist_ok=True)
24
+ logs_file = os.path.join(cls._LOGS_DIR, f"{__name__}-{name}.log")
25
+ _id = os.urandom(4).hex()
26
+ logger = logging.getLogger(_id)
27
+ if args_logging_level.lower() in cls._LOGGING_LEVELS:
28
+ logger.setLevel(cls._LOGGING_LEVELS[args_logging_level.lower()])
29
+ else:
30
+ logger.setLevel(cls._LOGGING_LEVELS["info"])
31
+ handler = RotatingFileHandler(
32
+ filename=logs_file,
33
+ maxBytes=1024 ** 2,
34
+ backupCount=1
35
+ )
36
+ fmt = (
37
+ "%(asctime)s.%(msecs)03d | %(levelname)s | %(name)s | "
38
+ "%(module)s | %(funcName)s | %(message)s"
39
+ )
40
+ datefmt = "%Y-%m-%d %H:%M:%S"
41
+ formatter = logging.Formatter(fmt=fmt, datefmt=datefmt)
42
+ handler.setFormatter(formatter)
43
+ logger.addHandler(handler)
44
+ return logger
@@ -3,7 +3,7 @@ import sys
3
3
  import atexit
4
4
  from python4cpm.secrets import Secrets
5
5
  from python4cpm.args import Args
6
- from python4cpm.logger import get_logger
6
+ from python4cpm.logger import Logger
7
7
 
8
8
 
9
9
  class Python4CPM:
@@ -27,15 +27,16 @@ class Python4CPM:
27
27
  def __init__(self, name: str) -> None:
28
28
  self._name = name
29
29
  self._args = self._get_args()
30
- self._logger = get_logger(
30
+ self._logger = Logger.get_logger(
31
31
  self._name,
32
32
  self._args.logging,
33
33
  self._args.logging_level
34
34
  )
35
- self.log_info("Python4CPM.__init__: initiating...")
36
- self._log_args()
35
+ self.log_debug("Initiating...")
36
+ self._log_env(self._args, False)
37
37
  self._verify_action()
38
38
  self._secrets = self._get_secrets()
39
+ self._log_env(self._secrets)
39
40
  self._closed = False
40
41
  atexit.register(self._on_exit)
41
42
 
@@ -49,19 +50,19 @@ class Python4CPM:
49
50
 
50
51
  def log_debug(self, message: str) -> None:
51
52
  if self._logger is not None:
52
- self._logger.debug(message)
53
+ self._logger.debug(message, stacklevel=2)
53
54
 
54
55
  def log_info(self, message: str) -> None:
55
56
  if self._logger is not None:
56
- self._logger.info(message)
57
+ self._logger.info(message, stacklevel=2)
57
58
 
58
59
  def log_warning(self, message: str) -> None:
59
60
  if self._logger is not None:
60
- self._logger.warning(message)
61
+ self._logger.warning(message, stacklevel=2)
61
62
 
62
63
  def log_error(self, message: str) -> None:
63
64
  if self._logger is not None:
64
- self._logger.error(message)
65
+ self._logger.error(message, stacklevel=2)
65
66
 
66
67
  @classmethod
67
68
  def _get_env_key(cls, key: str) -> str:
@@ -80,46 +81,41 @@ class Python4CPM:
80
81
  for secret in Secrets.SECRETS:
81
82
  _secret = os.environ.get(self._get_env_key(secret))
82
83
  secrets[secret] = _secret if _secret is not None else ""
83
- common_message = f"Python4CPM._get_secrets: {secret} ->"
84
- if secrets[secret]:
85
- self.log_info(f"{common_message} [SET]")
86
- else:
87
- self.log_info(f"{common_message} [NOT SET]")
88
84
  return Secrets(**secrets)
89
85
 
90
86
  def _verify_action(self) -> None:
91
87
  if self._args.action not in self._VALID_ACTIONS:
92
- self.log_warning(
93
- f"Python4CPM._verify_action: unkonwn action -> {self._args.action}"
94
- )
88
+ self.log_warning(f"Unkonwn action -> '{self._args.action}'")
95
89
 
96
- def _log_args(self) -> None:
97
- for key, value in vars(self._args).items():
98
- common_message = f"Python4CPM._log_args: {key.strip('_')} ->"
90
+ def _log_env(self, obj: object, masked: bool = True) -> None:
91
+ for key, value in vars(obj).items():
92
+ _key = key.strip('_')
99
93
  if value:
100
- self.log_info(f"{common_message} {value}")
94
+ if masked:
95
+ logging_value = "[SET]"
96
+ else:
97
+ logging_value = value
101
98
  else:
102
- self.log_info(f"{common_message} [NOT SET]")
99
+ logging_value = "[NOT SET]"
100
+ self.log_debug(f"{_key} -> '{logging_value}'")
103
101
 
104
102
  def close_fail(self, unrecoverable: bool = False) -> None:
105
103
  if unrecoverable is False:
106
104
  code = self._FAILED_RECOVERABLE_CODE
107
105
  else:
108
106
  code = self._FAILED_UNRECOVERABLE_CODE
109
- self.log_error(f"Python4CPM.close_fail: closing with code {code}")
107
+ self.log_error(f"Closing with code {code}")
110
108
  self._closed = True
111
109
  sys.exit(code)
112
110
 
113
111
  def close_success(self) -> None:
114
- self.log_info(
115
- f"Python4CPM.close_success: closing with code {self._SUCCESS_CODE}"
116
- )
112
+ self.log_debug(f"Closing with code {self._SUCCESS_CODE}")
117
113
  self._closed = True
118
114
  sys.exit(self._SUCCESS_CODE)
119
115
 
120
116
  def _on_exit(self):
121
117
  if self._closed is False:
122
- message = "Python4CPM._on_exit: no close signal called"
118
+ message = "No close signal called"
123
119
  self.log_error(message)
124
120
  sys.stderr.write(message)
125
121
  sys.stderr.flush()
@@ -1,30 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python4cpm
3
- Version: 1.0.22
3
+ Version: 1.0.23
4
4
  Summary: Python for CPM
5
5
  Author-email: Gonzalo Atienza Rela <gonatienza@gmail.com>
6
- License: MIT License
7
-
8
- Copyright (c) 2026 Gonzalo Atienza Rela
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in
18
- all copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
- THE SOFTWARE.
27
-
6
+ License-Expression: MIT
28
7
  Requires-Python: >=3.10
29
8
  Description-Content-Type: text/markdown
30
9
  License-File: LICENSE
@@ -32,7 +11,7 @@ Dynamic: license-file
32
11
 
33
12
  # Python4CPM
34
13
 
35
- A simple way of using python scripts with CyberArk CPM rotations. This module leverages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
14
+ A simple way of using python scripts with CyberArk CPM/SRS rotations. This module leverages the [Credential Management .NET SDK](https://docs.cyberark.com/privilege-cloud-standard/latest/en/content/pasimp/plug-in-netinvoker.htm) from CyberArk to securely offload a password rotation logic into a python script.
36
15
 
37
16
  This platform allows you to duplicate it multiple times, simply changing its settings from Privilege Cloud/PVWA to point to different python scripts leveraging the module `python4cpm`.
38
17
 
@@ -40,14 +19,14 @@ This platform allows you to duplicate it multiple times, simply changing its set
40
19
 
41
20
  ### Preparing Python
42
21
 
43
- 1. Install Python in CPM.
22
+ 1. Install Python along CPM or the SRS Connector Management Agent.
44
23
  - **Python must be installed for all users**. Follow the custom install steps from the installation wizard to check the checkbox.
45
- 3. Create a venv in CPM, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
24
+ 3. Create a venv in the server, by running `py -m venv c:\venv`. If desired, use a custom location and adjust any future references.
46
25
  4. Install `python4cpm` in your venv:
47
26
  - If your CPM can connect to the internet, install with `c:\venv\Scripts\pip install python4cpm`.
48
27
  - If your CPM cannot connect to the internet:
49
28
  - Download the latest `python4cpm-*.whl` file from the [pypi project files](https://pypi.org/project/python4cpm/#files).
50
- - Copy the file to CPM and extract to a temporary directory called `python4cpm-wheel`.
29
+ - Copy the file to the server into a temporary directory called `python4cpm-wheel`.
51
30
  - From the parent directory of `python4cpm-wheel` run `c:\venv\Scripts\pip install --no-index --find-links=.\python4cpm-wheel python4cpm`.
52
31
 
53
32
 
@@ -84,7 +63,7 @@ This platform allows you to duplicate it multiple times, simply changing its set
84
63
  from python4cpm import Python4CPMHandler
85
64
 
86
65
 
87
- class MyApp(Python4CPMHandler): # create a subclass for the Handler
66
+ class MyRotator(Python4CPMHandler): # create a subclass for the Handler
88
67
  """
89
68
  These are the usable properties and methods from Python4CPMHandler:
90
69
 
@@ -101,13 +80,13 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
101
80
 
102
81
  Logging methods -> Will only log if PythonLogging (platform parameters) is set to yes (default is yes)
103
82
 
104
- self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyApp.log
105
- self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyApp.log
106
- self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyApp.log
83
+ self.log_error("this is an error message") # logs error into Logs/ThirdParty/MyRotator.log
84
+ self.log_warning("this is a warning message") # logs warning into Logs/ThirdParty/MyRotator.log
85
+ self.log_info("this is an info message") # logs info into Logs/ThirdParty/MyRotator.log
107
86
 
108
87
  Logging level -> Will only log debug messages if PythonLoggingLevel (platform parameters) is set to debug (default is info)
109
88
 
110
- self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyApp.log if logging level is set to debug
89
+ self.log_debug("this is an debug message") # logs info into Logs/ThirdParty/MyRotator.log if logging level is set to debug
111
90
 
112
91
  =============================
113
92
  REQUIRED TERMINATION SIGNALS
@@ -160,9 +139,9 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
160
139
  # for your logic in a verification
161
140
  result = True
162
141
  if result is True:
163
- self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyApp.log
142
+ self.log_info("verification successful") # logs info message into Logs/ThirdParty/MyRotator.log
164
143
  else:
165
- self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyApp.log
144
+ self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
166
145
  self.close_fail()
167
146
 
168
147
  def _change(self, from_reconcile=False):
@@ -176,26 +155,26 @@ class MyApp(Python4CPMHandler): # create a subclass for the Handler
176
155
  # self.secrets.reconcile_password.get() and self.secrets.new_password.get() for your logic in a reconciliation
177
156
  result = True
178
157
  if result is True:
179
- self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyApp.log
158
+ self.log_info("rotation successful") # logs info message into Logs/ThirdParty/MyRotator.log
180
159
  else:
181
- self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyApp.log
160
+ self.log_error("something went wrong") # logs error message Logs/ThirdParty/MyRotator.log
182
161
  self.close_fail()
183
162
 
184
163
 
185
164
  if __name__ == "__main__":
186
- MyApp().run() # initializes the class and calls the action that was requested from CPM/SRS.
165
+ MyRotator().run() # initializes the class and calls the action that was requested from CPM/SRS.
187
166
  ```
188
167
  (*) More realistic examples can be found [here](https://github.com/gonatienza/python4cpm/blob/main/examples/python4cpmhandler).
189
168
 
190
169
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
191
- 1. Verify -> the sciprt will be executed once with the `MyApp.verify()` method.
192
- 2. Change -> the sciprt will be executed twice, once with the action `MyApp.logon()` method and once as `MyApp.change()` method.
193
- - If all actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
194
- 3. Reconcile -> the sciprt will be executed twice, once with the action `MyApp.prereconcile()` method and once as `MyApp.reconcile()` method.
195
- - If all actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
196
- 4. When calling `MyApp.logon()` or `MyApp.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
197
- 5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return an empty string.
198
- 6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return an empty string.
170
+ 1. Verify -> the sciprt will be executed once running the `MyRotator.verify()` method.
171
+ 2. Change -> the sciprt will be executed twice, running first the `MyRotator.logon()` method and secondly the `MyRotator.change()` method.
172
+ - If both actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
173
+ 3. Reconcile -> the sciprt will be executed twice, running first the `MyRotator.prereconcile()` method and secondly the `MyRotator.reconcile()` method.
174
+ - If both actions are not terminated with `self.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `self.close_fail(unrecoverable=True)`.
175
+ 4. When calling `MyRotator.verify()`, `MyRotator.logon()` or `MyRotator.prereconcile()`: `self.secrets.new_password.get()` will always return an empty string.
176
+ 5. If a logon account is not linked, `self.args.logon_username` and `self.secrets.logon_password.get()` will return empty strings.
177
+ 6. If a reconcile account is not linked, `self.args.reconcile_username` and `self.secrets.reconcile_password.get()` will return empty strings.
199
178
 
200
179
 
201
180
  ### Using Python4CPM properties and methods directly (for low level controls):
@@ -299,12 +278,12 @@ if __name__ == "__main__":
299
278
  When doing `verify`, `change` or `reconcile` from Privilege Cloud/PVWA:
300
279
  1. Verify -> the sciprt will be executed once with the `p4cpm.args.action` as `Python4CPM.ACTION_VERIFY`.
301
280
  2. Change -> the sciprt will be executed twice, once with the action `p4cpm.args.action` as `Python4CPM.ACTION_LOGON` and once as `Python4CPM.ACTION_CHANGE`.
302
- - If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
281
+ - If both actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
303
282
  3. Reconcile -> the sciprt will be executed twice, once with the `p4cpm.args.action` as `Python4CPM.ACTION_PRERECONCILE` and once as `Python4CPM.ACTION_RECONCILE`.
304
- - If all actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
283
+ - If both actions are not terminated with `p4cpm.close_success()` and the scripts terminates without any exception, CPM/SRS will see this as a `p4cpm.close_fail(unrecoverable=True)`.
305
284
  4. When `p4cpm.args.action` comes as `Python4CPM.ACTION_VERIFY`, `Python4CPM.ACTION_LOGON` or `Python4CPM.ACTION_PRERECONCILE`: `p4cpm.secrets.new_password.get()` will always return an empty string.
306
- 5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return an empty string.
307
- 6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return an empty string.
285
+ 5. If a logon account is not linked, `p4cpm.args.logon_username` and `p4cpm.secrets.logon_password.get()` will return empty strings.
286
+ 6. If a reconcile account is not linked, `p4cpm.args.reconcile_username` and `p4cpm.secrets.reconcile_password.get()` will return empty strings.
308
287
 
309
288
 
310
289
  ### Installing dependencies in python venv
@@ -329,6 +308,8 @@ pip install python4cpm
329
308
 
330
309
  ### Example:
331
310
 
311
+ #### Set your arguments and secrets:
312
+
332
313
  ```python
333
314
  from python4cpm import NETHelper, Python4CPM, Python4CPMHandler
334
315
  from getpass import getpass
@@ -339,11 +320,7 @@ password = getpass("password: ") # password from account
339
320
  logon_password = getpass("logon_password: ") # password from linked logon account
340
321
  reconcile_password = getpass("reconcile_password: ") # password from linked reconcile account
341
322
  new_password = getpass("new_password: ") # new password for the rotation
342
- ```
343
-
344
- #### Set your arguments and secrets:
345
323
 
346
- ```python
347
324
  NETHelper.set(
348
325
  action=Python4CPM.ACTION_LOGON, # use actions from Python4CPM.ACTION_*
349
326
  address="myapp.corp.local", # populate with the address from your account properties
@@ -362,7 +339,7 @@ NETHelper.set(
362
339
  #### Using the handler (recommended):
363
340
 
364
341
  ```python
365
- class MyApp(Python4CPMHandler):
342
+ class MyRotator(Python4CPMHandler):
366
343
  def verify(self):
367
344
  # TODO: Add your logic here
368
345
  self.close_success()
@@ -383,7 +360,7 @@ class MyApp(Python4CPMHandler):
383
360
  # TODO: Add your logic here
384
361
  self.close_success()
385
362
 
386
- MyApp().run()
363
+ MyRotator().run()
387
364
  ```
388
365
 
389
366
  #### Using Python4CPM properties and methods directly:
@@ -399,6 +376,7 @@ p4cpm.close_success()
399
376
 
400
377
  #### Remember for your final script:
401
378
 
402
- - Change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")` if you are using the properties and methods directly.
403
- - Remove any secrets prompting or interactive interruptions.
404
379
  - Remove the import of `NETHelper`.
380
+ - Remove the `NETHelper.set()` call.
381
+ - If applicable, change the definition of `p4cpm` from `p4cpm = NETHelper.get()` to `p4cpm = Python4CPM("MyApp")`.
382
+ - Remove any secrets prompting or interactive interruptions.
@@ -1,40 +0,0 @@
1
- import os
2
- import logging
3
- from logging.handlers import RotatingFileHandler
4
-
5
-
6
- _LOGS_DIR = os.path.join("Logs", "ThirdParty")
7
- _LOGGING_ENABLED_VALUE = "yes"
8
- _LOGGING_LEVELS = {
9
- "info": logging.INFO,
10
- "debug": logging.DEBUG
11
- }
12
-
13
-
14
- def get_logger(
15
- name: str,
16
- args_logging: str,
17
- args_logging_level: str
18
- ) -> logging.Logger:
19
- if args_logging.lower() != _LOGGING_ENABLED_VALUE:
20
- return None
21
- os.makedirs(_LOGS_DIR, exist_ok=True)
22
- logs_file = os.path.join(_LOGS_DIR, f"{__name__}-{name}.log")
23
- _id = os.urandom(4).hex()
24
- logger = logging.getLogger(_id)
25
- if args_logging_level.lower() in _LOGGING_LEVELS:
26
- logger.setLevel(_LOGGING_LEVELS[args_logging_level.lower()])
27
- else:
28
- logger.setLevel(_LOGGING_LEVELS["info"])
29
- handler = RotatingFileHandler(
30
- filename=logs_file,
31
- maxBytes=1024 ** 2,
32
- backupCount=1
33
- )
34
- formatter = logging.Formatter(
35
- fmt="%(asctime)s | %(name)s | %(levelname)s | %(message)s",
36
- datefmt="%Y-%m-%d %H:%M:%S"
37
- )
38
- handler.setFormatter(formatter)
39
- logger.addHandler(handler)
40
- return logger
File without changes
File without changes