launchpy 0.5.1__tar.gz → 0.5.3__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.
Files changed (40) hide show
  1. launchpy-0.5.3/PKG-INFO +122 -0
  2. launchpy-0.5.3/README.md +91 -0
  3. {launchpy-0.5.1 → launchpy-0.5.3}/docs/cli.md +43 -0
  4. {launchpy-0.5.1 → launchpy-0.5.3}/docs/getstarted.md +55 -0
  5. {launchpy-0.5.1 → launchpy-0.5.3}/docs/main.md +11 -0
  6. {launchpy-0.5.1 → launchpy-0.5.3}/docs/releases.md +23 -0
  7. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/__init__.py +7 -3
  8. launchpy-0.5.3/launchpy/__version__.py +1 -0
  9. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/cli/__main__.py +21 -16
  10. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/configs.py +33 -5
  11. launchpy-0.5.3/launchpy/connector.py +198 -0
  12. launchpy-0.5.3/launchpy.egg-info/PKG-INFO +122 -0
  13. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy.egg-info/SOURCES.txt +3 -1
  14. launchpy-0.5.3/tests/test_connector.py +254 -0
  15. launchpy-0.5.3/tests/test_proxies.py +337 -0
  16. launchpy-0.5.1/PKG-INFO +0 -68
  17. launchpy-0.5.1/README.md +0 -37
  18. launchpy-0.5.1/launchpy/__version__.py +0 -1
  19. launchpy-0.5.1/launchpy/connector.py +0 -237
  20. launchpy-0.5.1/launchpy.egg-info/PKG-INFO +0 -68
  21. {launchpy-0.5.1 → launchpy-0.5.3}/LICENSE +0 -0
  22. {launchpy-0.5.1 → launchpy-0.5.3}/docs/admin.md +0 -0
  23. {launchpy-0.5.1 → launchpy-0.5.3}/docs/library.md +0 -0
  24. {launchpy-0.5.1 → launchpy-0.5.3}/docs/property.md +0 -0
  25. {launchpy-0.5.1 → launchpy-0.5.3}/docs/synchronizer.md +0 -0
  26. {launchpy-0.5.1 → launchpy-0.5.3}/docs/translator.md +0 -0
  27. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/admin.py +0 -0
  28. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/cli/__init__.py +0 -0
  29. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/config.py +0 -0
  30. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/launchpy.py +0 -0
  31. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/library.py +0 -0
  32. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/property.py +0 -0
  33. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy/synchronizer.py +0 -0
  34. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy.egg-info/dependency_links.txt +0 -0
  35. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy.egg-info/entry_points.txt +0 -0
  36. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy.egg-info/requires.txt +0 -0
  37. {launchpy-0.5.1 → launchpy-0.5.3}/launchpy.egg-info/top_level.txt +0 -0
  38. {launchpy-0.5.1 → launchpy-0.5.3}/pyproject.toml +0 -0
  39. {launchpy-0.5.1 → launchpy-0.5.3}/requirements.txt +0 -0
  40. {launchpy-0.5.1 → launchpy-0.5.3}/setup.cfg +0 -0
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: launchpy
3
+ Version: 0.5.3
4
+ Summary: Adobe Launch API wrapper
5
+ Author-email: Julien Piccini <piccini.julien@gmail.com>
6
+ License: Apache License 2.0
7
+ Project-URL: homepage, https://github.com/pitchmuc/launchpy
8
+ Project-URL: changelog, https://github.com/pitchmuc/launchpy/blob/master/docs/releases.md
9
+ Keywords: adobe,analytics,API,python
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Development Status :: 4 - Beta
19
+ Requires-Python: >=3.12
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: pandas
23
+ Requires-Dist: requests
24
+ Requires-Dist: pathlib2
25
+ Requires-Dist: pathlib
26
+ Requires-Dist: aepp
27
+ Requires-Dist: rich
28
+ Requires-Dist: deprecated
29
+ Requires-Dist: httpx
30
+ Dynamic: license-file
31
+
32
+ # Launchpy
33
+
34
+ Python wrapper for the Adobe Launch API.\
35
+ This module is named launchpy.
36
+
37
+ Current version: **0.5.2**. This version fixes GET retries and adds automatic
38
+ HTTP 429 retries across all five connector methods. See the
39
+ [0.5.2 release notes](./docs/releases.md#052) for details.
40
+
41
+ ## Installation
42
+
43
+ You can install the module by realising the following pip command:\
44
+ `pip install launchpy`
45
+
46
+ You can access the version of the module that you run via the following attribute:\
47
+ `launchpy.__version__`
48
+
49
+ ## Content of the module
50
+
51
+ This module is built around 7 main parts:
52
+
53
+ - Helping functions [Core Components](./docs/main.md)
54
+ - Admin instantiating functions [Admin Class](./docs/admin.md)
55
+ - Managing properties [Property Class](./docs/property.md)
56
+ - Managing Publishing Cycle [Library Class](./docs/library.md)
57
+ - Translator functionationality [Translator Class](./docs/translator.md)
58
+ - Synchronizer [Synchronizer Class](./docs/synchronizer.md)
59
+ - Command Line Interface [CLI](./docs/cli.md)
60
+
61
+ ## Get Started
62
+
63
+ A [get started guide](./docs/getstarted.md) has been created to explain the different functionality.
64
+ You can find a more detail description functionalities at [datanalyst.info](https://datanalyst.info).
65
+
66
+ ## Main documentation
67
+
68
+ Most of the documentation has been imported from the datanalyst website [here](./docs/main.md).
69
+
70
+ ## Release information
71
+
72
+ You can find release information [here](./docs/releases.md).
73
+
74
+ ## Proxy support
75
+
76
+ Pass a `proxy` URL to `launchpy.configure(...)`, or add `"proxy"` to
77
+ your JSON configuration file. The same proxy serves both HTTP and HTTPS.
78
+ Proxy settings apply to OAuth authentication,
79
+ API requests, and asynchronous property exports, with separate settings per
80
+ organization.
81
+
82
+ ```python
83
+ launchpy.configure(
84
+ org_id="your-org@AdobeOrg",
85
+ client_id="your-client-id",
86
+ secret="your-secret",
87
+ scopes="your-scopes",
88
+ proxy="http://proxy.example.com:8080",
89
+ )
90
+ ```
91
+
92
+ The CLI accepts `--proxy URL` at startup and in the interactive `config`
93
+ command. An explicit CLI proxy overrides the file setting.
94
+ Without an explicit proxy, existing environment-based proxy behavior is unchanged.
95
+ See the [Python and JSON examples](./docs/getstarted.md#proxy-configuration)
96
+ and [CLI examples](./docs/cli.md#proxy-configuration).
97
+
98
+ ## HTTP retries
99
+
100
+ The `AdobeRequest` connector always retries HTTP 429 responses for GET, POST,
101
+ PUT, PATCH, and DELETE, even when their body is not JSON or `retry=0`.
102
+ Rate-limit retries have no attempt limit and do not consume the configured
103
+ `retry` budget. A request can therefore keep waiting while the API remains
104
+ rate limited; it can be interrupted normally.
105
+
106
+ The connector
107
+ honors `Retry-After` in seconds or HTTP-date format; absent or invalid values
108
+ use exponential backoff starting at 45 seconds and capped at 300 seconds.
109
+ The JSON-returning methods also always retry Adobe error code `429050`.
110
+
111
+ The `retry` setting is reserved for other retryable errors: currently, GET
112
+ responses that cannot be decoded as JSON. It specifies the number of
113
+ **additional attempts**, with a default of `0`. Set
114
+ `property.connector.retry = 3` or pass `retry=3` to a connector method to
115
+ override this budget; neither setting limits rate-limit retries.
116
+
117
+ Successful return values are unchanged: DELETE returns the HTTP status code,
118
+ while the other methods return decoded JSON. GET also retries invalid JSON
119
+ responses with a 30-second delay. Invalid JSON from write operations and
120
+ network exceptions do not trigger retries, to avoid replaying writes that
121
+ may already have succeeded. This policy applies to the five connector methods,
122
+ not the separate OAuth token request or async property-extraction requests.
@@ -0,0 +1,91 @@
1
+ # Launchpy
2
+
3
+ Python wrapper for the Adobe Launch API.\
4
+ This module is named launchpy.
5
+
6
+ Current version: **0.5.2**. This version fixes GET retries and adds automatic
7
+ HTTP 429 retries across all five connector methods. See the
8
+ [0.5.2 release notes](./docs/releases.md#052) for details.
9
+
10
+ ## Installation
11
+
12
+ You can install the module by realising the following pip command:\
13
+ `pip install launchpy`
14
+
15
+ You can access the version of the module that you run via the following attribute:\
16
+ `launchpy.__version__`
17
+
18
+ ## Content of the module
19
+
20
+ This module is built around 7 main parts:
21
+
22
+ - Helping functions [Core Components](./docs/main.md)
23
+ - Admin instantiating functions [Admin Class](./docs/admin.md)
24
+ - Managing properties [Property Class](./docs/property.md)
25
+ - Managing Publishing Cycle [Library Class](./docs/library.md)
26
+ - Translator functionationality [Translator Class](./docs/translator.md)
27
+ - Synchronizer [Synchronizer Class](./docs/synchronizer.md)
28
+ - Command Line Interface [CLI](./docs/cli.md)
29
+
30
+ ## Get Started
31
+
32
+ A [get started guide](./docs/getstarted.md) has been created to explain the different functionality.
33
+ You can find a more detail description functionalities at [datanalyst.info](https://datanalyst.info).
34
+
35
+ ## Main documentation
36
+
37
+ Most of the documentation has been imported from the datanalyst website [here](./docs/main.md).
38
+
39
+ ## Release information
40
+
41
+ You can find release information [here](./docs/releases.md).
42
+
43
+ ## Proxy support
44
+
45
+ Pass a `proxy` URL to `launchpy.configure(...)`, or add `"proxy"` to
46
+ your JSON configuration file. The same proxy serves both HTTP and HTTPS.
47
+ Proxy settings apply to OAuth authentication,
48
+ API requests, and asynchronous property exports, with separate settings per
49
+ organization.
50
+
51
+ ```python
52
+ launchpy.configure(
53
+ org_id="your-org@AdobeOrg",
54
+ client_id="your-client-id",
55
+ secret="your-secret",
56
+ scopes="your-scopes",
57
+ proxy="http://proxy.example.com:8080",
58
+ )
59
+ ```
60
+
61
+ The CLI accepts `--proxy URL` at startup and in the interactive `config`
62
+ command. An explicit CLI proxy overrides the file setting.
63
+ Without an explicit proxy, existing environment-based proxy behavior is unchanged.
64
+ See the [Python and JSON examples](./docs/getstarted.md#proxy-configuration)
65
+ and [CLI examples](./docs/cli.md#proxy-configuration).
66
+
67
+ ## HTTP retries
68
+
69
+ The `AdobeRequest` connector always retries HTTP 429 responses for GET, POST,
70
+ PUT, PATCH, and DELETE, even when their body is not JSON or `retry=0`.
71
+ Rate-limit retries have no attempt limit and do not consume the configured
72
+ `retry` budget. A request can therefore keep waiting while the API remains
73
+ rate limited; it can be interrupted normally.
74
+
75
+ The connector
76
+ honors `Retry-After` in seconds or HTTP-date format; absent or invalid values
77
+ use exponential backoff starting at 45 seconds and capped at 300 seconds.
78
+ The JSON-returning methods also always retry Adobe error code `429050`.
79
+
80
+ The `retry` setting is reserved for other retryable errors: currently, GET
81
+ responses that cannot be decoded as JSON. It specifies the number of
82
+ **additional attempts**, with a default of `0`. Set
83
+ `property.connector.retry = 3` or pass `retry=3` to a connector method to
84
+ override this budget; neither setting limits rate-limit retries.
85
+
86
+ Successful return values are unchanged: DELETE returns the HTTP status code,
87
+ while the other methods return decoded JSON. GET also retries invalid JSON
88
+ responses with a 30-second delay. Invalid JSON from write operations and
89
+ network exceptions do not trigger retries, to avoid replaying writes that
90
+ may already have succeeded. This policy applies to the five connector methods,
91
+ not the separate OAuth token request or async property-extraction requests.
@@ -10,6 +10,7 @@ This document provides an overview of the command line interface (CLI) for `laun
10
10
  - [Instantiation](#instantiation)
11
11
  - [create\_config\_file](#create_config_file)
12
12
  - [config](#config)
13
+ - [Proxy configuration](#proxy-configuration)
13
14
  - [change\_org](#change_org)
14
15
  - [get\_properties](#get_properties)
15
16
  - [create\_property](#create_property)
@@ -148,6 +149,7 @@ Arguments
148
149
  `-cf`,`--config_file`: The path to the config file that contains your credentials and other necessary information.
149
150
  `-p`,`--property` : The property name to be used and directly instantiated the property layer. This is an optional parameter.
150
151
  `-on`,`--org_name`: The organization name to use when your `--config_file` is a multi-org config file (see [create_config_file](#create_config_file)). This is an optional parameter, it defaults to the first organization found in the file (or `default` when using individual parameters).
152
+ `--proxy`: Proxy URL for both HTTP and HTTPS destinations.
151
153
 
152
154
  Example via parameters:
153
155
  ```bash
@@ -195,6 +197,47 @@ launchpy:client_a>
195
197
  When in a non-multi-org environment, the prompt of the CLI will be `launchpy>`, and when in a multi-org environment, the prompt will be `launchpy:<org_name>`, where `<org_name>` is the name of the organization you are currently connected to.\
196
198
  By default, the first organization found in the multi-org config file will be used if no `--org_name` is provided. You can switch between organizations using the `change_org` command.
197
199
 
200
+ #### Proxy configuration
201
+
202
+ The single `--proxy` option works both at startup and in the interactive `config` command,
203
+ with either a config file or individual credentials:
204
+
205
+ ```bash
206
+ python -m launchpy.cli --config_file config.json --proxy http://proxy.example.com:8080
207
+ ```
208
+
209
+ ```text
210
+ (Cmd) config --config_file config.json --proxy http://proxy.example.com:8080
211
+ (Cmd) config --client_id <client_id> --secret <secret> --org_id <org_id> --scopes <scopes> --proxy http://proxy.example.com:8080
212
+ ```
213
+
214
+ You can also store a single `proxy` URL in the JSON file:
215
+
216
+ ```json
217
+ {
218
+ "org_id": "your-org@AdobeOrg",
219
+ "client_id": "your-client-id",
220
+ "secret": "your-secret",
221
+ "scopes": "your-scopes",
222
+ "proxy": "http://proxy.example.com:8080"
223
+ }
224
+ ```
225
+
226
+ The same proxy is used for both HTTP and HTTPS destinations. `--proxy` overrides
227
+ the file's proxy URL. For multi-org files, put `proxy` in each organization's
228
+ object; `--proxy` overrides it for every organization loaded from that file.
229
+ `change_org` preserves each organization's effective proxy settings.
230
+
231
+ The proxy covers authentication, token refresh, API calls, and property
232
+ exports. Existing files and commands without proxy options continue to work,
233
+ including environment-based proxy settings. Omitting `proxy` or setting it to
234
+ `null` in JSON does not disable environment proxies. Empty strings and non-string
235
+ values are rejected. TLS verification remains
236
+ enabled. For URL schemes, authentication, and Python configuration examples,
237
+ see [Proxy configuration in Getting Started](./getstarted.md#proxy-configuration).
238
+ Avoid putting proxy credentials on a shared terminal's command line or in
239
+ committed configuration files.
240
+
198
241
  #### change_org
199
242
  Switch the current CLI session to another organization that has already been loaded from a multi-org config file (via `config` or `--config_file` at startup). This avoids re-running `config` when you need to move between organizations.\
200
243
  Arguments:
@@ -101,6 +101,61 @@ So your config file will look like this:
101
101
  ```
102
102
 
103
103
 
104
+ ### Proxy configuration
105
+
106
+ To connect through a proxy, pass the optional `proxy` URL to `configure`.
107
+ The same proxy is used for both HTTP and HTTPS:
108
+
109
+ ```python
110
+ lp.configure(
111
+ org_id="your-org@AdobeOrg",
112
+ client_id="your-client-id",
113
+ secret="your-secret",
114
+ scopes="your-scopes",
115
+ proxy="http://proxy.example.com:8080",
116
+ )
117
+ ```
118
+
119
+ Alternatively, add `proxy` to your configuration JSON and load it with
120
+ `lp.importConfigFile("myconfig.json")` as usual:
121
+
122
+ ```json
123
+ {
124
+ "org_id": "your-org@AdobeOrg",
125
+ "client_id": "your-client-id",
126
+ "secret": "your-secret",
127
+ "scopes": "your-scopes",
128
+ "proxy": "http://proxy.example.com:8080"
129
+ }
130
+ ```
131
+
132
+ The proxy URL includes its scheme and port.
133
+ An HTTPS destination commonly uses an `http://` proxy
134
+ URL, which tunnels HTTPS with CONNECT; use `https://` only if the proxy itself
135
+ requires TLS. TLS certificate verification remains enabled.
136
+
137
+ For proxy authentication, use `http://username:password@proxy.example.com:8080`,
138
+ URL-encoding special characters in the username and password. Treat files and
139
+ command-line arguments containing proxy credentials as secrets.
140
+
141
+ In a multi-org JSON file, put `proxy` inside each organization's object.
142
+ Organizations can use different proxies, or omit the field independently.
143
+ To override the file's proxy for every imported organization, use
144
+ `lp.importConfigFile("myconfig.json", proxy="http://other-proxy:8080")`.
145
+
146
+ The settings apply to initial OAuth token acquisition, token refresh, all API
147
+ methods and their retries, and asynchronous rule-component downloads during
148
+ `extractProperty`. Configure before creating `Admin`, `Property`, `Library`,
149
+ or `Synchronizer` instances; existing connectors retain their configuration.
150
+ Calling `configure` again without `proxy` clears the previous explicit
151
+ proxy for that organization.
152
+
153
+ Omitting `proxy` or passing `None` preserves the HTTP libraries'
154
+ normal environment-based behavior (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`,
155
+ and `NO_PROXY`). This does **not** disable environment proxies.
156
+ Empty strings and non-string proxy values are rejected.
157
+ Existing configuration files without a proxy require no changes.
158
+
104
159
  ## 5. Instantiate the Admin class & get Company ID to retrieve properties
105
160
 
106
161
  Once all of these setup steps are completed, you can instantiate the `Admin` class.
@@ -37,6 +37,11 @@ launchpy.importConfigFile('myConfig.json')
37
37
  launchpy.config.config_object
38
38
  ```
39
39
 
40
+ Configuration JSON files can include an optional `proxy` URL, independently
41
+ for each organization in multi-org files. `importConfigFile` also accepts
42
+ `proxy=...` to override the file's proxy for all imported organizations.
43
+ See [Proxy configuration](./getstarted.md#proxy-configuration) for examples.
44
+
40
45
  ### Configure method
41
46
 
42
47
  The configure method enables you to configure the `config_object` directly from the python console with environment.\
@@ -56,6 +61,12 @@ lp.configure(
56
61
  )
57
62
  ```
58
63
 
64
+ `configure` also accepts `proxy="http://proxy.example.com:8080"`.
65
+ This single URL is used for both HTTP and HTTPS destinations. These settings
66
+ apply to OAuth, API requests, and property exports. Omit the argument to retain
67
+ normal environment-based proxy behavior; configuring an organization again
68
+ without it clears that organization's previous explicit proxy.
69
+
59
70
  ## 1. The Admin class
60
71
 
61
72
  The Admin methods helps your retrieving vital information to use the API later, such as the companyId.\
@@ -3,6 +3,29 @@
3
3
  This page gathered the changes made between version of the launchpy module.\
4
4
  This has been started after the 0.3.0 release.\
5
5
 
6
+ ## 0.5.3
7
+ * Adding optional `proxy` support to `configure`, `importConfigFile`, `createConfigFile` and JSON configuration files. A single proxy URL is used for both HTTP and HTTPS, with separate settings per organization.
8
+ * Applying proxy settings to OAuth token acquisition and refresh, all API requests and their retries, and asynchronous rule-component downloads during `extractProperty`.
9
+ * Adding `--proxy` to CLI startup and the interactive `config` command. Explicit CLI and `importConfigFile` proxy arguments override file settings for all organizations loaded from the file.
10
+ * Preserving environment-based proxy behavior when no explicit proxy is supplied. Existing configuration files require no changes; empty strings and non-string proxy values are rejected.
11
+ * Fixing CLI configuration state and organization selection when `--org_name` is omitted. `change_org` now uses the shared connection setup and preserves each organization's proxy settings.
12
+ * Adding proxy configuration examples and documentation for Python, JSON, and CLI usage.
13
+
14
+ ## 0.5.2
15
+ * Fixing recursive GET retries and always retrying HTTP 429 responses for GET, POST, PUT, PATCH, and DELETE.
16
+ * Respecting `Retry-After` seconds and HTTP dates, with exponential backoff when the header is absent or invalid.
17
+ * Keeping rate-limit retries independent of the connector's `retry` budget and per-call overrides, including when `retry=0`. Other retryable errors remain bounded by that budget.
18
+ * Preserving DELETE request bodies when sending and retrying requests.
19
+
20
+ ## 0.5.1
21
+ * Raising the minimum supported Python version from 3.10 to 3.12.
22
+ * `syncComponent` now returns a dictionary of per-target status messages. `syncComponents`, `syncRules`, `syncDataElements`, `syncExtensions`, and `syncFromLibrary` now return results grouped by component name or ID, including error messages when library synchronization fails.
23
+ * Updating the CLI synchronization commands to report missing components instead of always reporting success. Successful per-target operations are displayed when verbose mode is enabled.
24
+ * Adding the `-v` / `--verbose` flag to the `sync_rules`, `sync_data_elements`, and `upgrade_extension` CLI commands.
25
+ * `checkExtensionUpdate` now raises a descriptive error when the requested extension is not installed, including the installed extension names.
26
+ * `upgradeTargetExtension` now respects the `platform` parameter and returns per-target upgrade, already-up-to-date, or failure messages. The CLI displays upgrade failures instead of reporting success.
27
+ * Fixing library-linked component preparation when no library component was retrieved, avoiding an uninitialized-variable error.
28
+
6
29
  ## 0.5.0
7
30
  * Fixing the `create_property` method in the CLI
8
31
  * Adding support for multi-org config files.
@@ -5,6 +5,7 @@ May 15 12:04:49 2019
5
5
  """
6
6
  from launchpy.__version__ import __version__
7
7
  from launchpy.configs import *
8
+ from launchpy.configs import _validate_proxy
8
9
  from launchpy.library import Library
9
10
  from launchpy.admin import Admin
10
11
  from launchpy.property import Property
@@ -42,10 +43,13 @@ async def __extractRuleComponents__(rule: dict, client: httpx.AsyncClient, folde
42
43
  json.dump(component, f, indent=4)
43
44
 
44
45
  # 3. Use an orchestrator instead of ThreadPoolExecutor
45
- async def process_all_rules(rules_list, folder, header):
46
+ async def process_all_rules(rules_list, folder, header,
47
+ proxy: str | None = None):
48
+ proxy = _validate_proxy(proxy)
46
49
  timeout = httpx.Timeout(10.0, read=120.0)
47
50
  limits = httpx.Limits(max_connections=100, max_keepalive_connections=50)
48
- async with httpx.AsyncClient(timeout=timeout, limits=limits) as client:
51
+ proxy_kwargs = {"proxy": proxy} if proxy is not None else {}
52
+ async with httpx.AsyncClient(timeout=timeout, limits=limits, **proxy_kwargs) as client:
49
53
  # Create a list of "tasks" to run
50
54
  tasks = [__extractRuleComponents__(rule, client, folder, header) for rule in rules_list]
51
55
  # 'gather' runs them all concurrently
@@ -90,7 +94,7 @@ def extractProperty(property: dict | Property, publishedVersion: bool = False,fo
90
94
  except Exception as e:
91
95
  published_rules.append(rule)
92
96
  rules = published_rules
93
- asyncio.run(process_all_rules(rules, folder, property.header))
97
+ asyncio.run(process_all_rules(rules, folder, property.header, property.connector.proxy))
94
98
  dataElements = property.getDataElements()
95
99
  if publishedVersion:
96
100
  published_data_elements = []
@@ -0,0 +1 @@
1
+ __version__ = "0.5.3"
@@ -1,5 +1,6 @@
1
1
  import launchpy
2
2
  from launchpy.config import config_objects, headers
3
+ from launchpy.configs import _validate_proxy
3
4
  import argparse, cmd, shlex, json
4
5
  from functools import wraps
5
6
  from rich.console import Console
@@ -961,6 +962,7 @@ class MainShell(cmd.Cmd):
961
962
 
962
963
  def __init__(self, **kwargs:ParamSpecKwargs) -> None:
963
964
  super().__init__()
965
+ proxy = _validate_proxy(kwargs.get("proxy"))
964
966
  self.admin = None
965
967
  self.cid = None
966
968
  self.properties = None
@@ -988,12 +990,13 @@ class MainShell(cmd.Cmd):
988
990
  with open(config_path, "rb") as f:
989
991
  dict_config = json.load(f)
990
992
  if type(dict_config) == dict:
991
- config_key = kwargs.get("org_name", "default")
993
+ config_key = kwargs.get("org_name") or "default"
992
994
  self.config = {config_key: deepcopy(self.config_template)}
993
995
  self.config[config_key]['secret'] = dict_config.get("secret", kwargs.get("secret"))
994
996
  self.config[config_key]['org_id'] = dict_config.get("org_id", kwargs.get("org_id"))
995
997
  self.config[config_key]['client_id'] = dict_config.get("client_id", dict_config.get("api_key", kwargs.get("client_id", kwargs.get("api_key"))))
996
998
  self.config[config_key]['scopes'] = dict_config.get("scopes", kwargs.get("scopes"))
999
+ self.config[config_key]['proxy'] = proxy if proxy is not None else _validate_proxy(dict_config.get("proxy"))
997
1000
  config_objects.update(self.config)
998
1001
  elif type(dict_config) == list:
999
1002
  self.config = {}
@@ -1007,15 +1010,17 @@ class MainShell(cmd.Cmd):
1007
1010
  self.config[config_key]['org_id'] = config.get("org_id", kwargs.get("org_id"))
1008
1011
  self.config[config_key]['client_id'] = config.get("client_id", config.get("api_key", kwargs.get("client_id", kwargs.get("api_key"))))
1009
1012
  self.config[config_key]['scopes'] = config.get("scopes", kwargs.get("scopes"))
1013
+ self.config[config_key]['proxy'] = proxy if proxy is not None else _validate_proxy(config.get("proxy"))
1010
1014
  config_objects.update(self.config)
1011
1015
  config_key = dict_config[0].get("org_name", "default")
1012
1016
  else:
1013
- config_key = kwargs.get("org_name", "default")
1017
+ config_key = kwargs.get("org_name") or "default"
1014
1018
  self.config = {config_key: deepcopy(self.config_template)}
1015
1019
  self.config[config_key]['secret'] = kwargs.get("secret")
1016
1020
  self.config[config_key]['org_id'] = kwargs.get("org_id")
1017
1021
  self.config[config_key]['client_id'] = kwargs.get("client_id",kwargs.get("api_key"))
1018
1022
  self.config[config_key]['scopes'] = kwargs.get("scopes")
1023
+ self.config[config_key]['proxy'] = proxy
1019
1024
  config_objects.update(self.config)
1020
1025
  if self.config[config_key]['secret'] is not None and self.config[config_key]['org_id'] is not None and self.config[config_key]['client_id'] is not None and self.config[config_key]['scopes'] is not None:
1021
1026
  myheaders = {}
@@ -1024,7 +1029,7 @@ class MainShell(cmd.Cmd):
1024
1029
  myheaders[org_name]["x-gw-ims-org-id"] = config['org_id']
1025
1030
  myheaders[org_name]["x-api-key"] = config['client_id']
1026
1031
  headers.update(myheaders)
1027
- config_key = kwargs.get("org_name", config_key)
1032
+ config_key = kwargs.get("org_name") or config_key
1028
1033
  console.print("Configuring connection...", style="blue")
1029
1034
  self._configure_connection(config_key)
1030
1035
  if kwargs.get("property") is not None:
@@ -1051,13 +1056,15 @@ class MainShell(cmd.Cmd):
1051
1056
  else:
1052
1057
  config_key = list(self.config.keys())[0]
1053
1058
  self.org_name = config_key
1054
- self.config = launchpy.configure(
1059
+ launchpy.configure(
1055
1060
  secret=config_objects[config_key]['secret'],
1056
1061
  org_id=config_objects[config_key]['org_id'],
1057
1062
  client_id=config_objects[config_key]['client_id'],
1058
1063
  scopes=config_objects[config_key]['scopes'],
1059
- org_name=config_key
1064
+ org_name=config_key,
1065
+ proxy=config_objects[config_key].get("proxy")
1060
1066
  )
1067
+ self.config = config_objects
1061
1068
  self.admin = launchpy.Admin(org_name=config_key)
1062
1069
  self.cid = self.admin.getCompanyId()
1063
1070
  if config_key == 'default' or config_key is None:
@@ -1075,16 +1082,7 @@ class MainShell(cmd.Cmd):
1075
1082
  if org_name not in config_objects.keys():
1076
1083
  console.print(f"(!) Error: Organization '{org_name}' not found in the configuration.", style="red")
1077
1084
  return
1078
- self.config = launchpy.configure(
1079
- secret=config_objects[org_name]['secret'],
1080
- org_id=config_objects[org_name]['org_id'],
1081
- client_id=config_objects[org_name]['client_id'],
1082
- scopes=config_objects[org_name]['scopes'],
1083
- org_name=org_name
1084
- )
1085
- self.admin = launchpy.Admin(org_name=org_name)
1086
- self.cid = self.admin.getCompanyId()
1087
- self.prompt = f"launchpy:{org_name}> "
1085
+ self._configure_connection(org_name)
1088
1086
  console.print(f"Switched to organization '{org_name}'.", style="green")
1089
1087
  except Exception as e:
1090
1088
  console.print(f"(!) Error: {str(e)}", style="red")
@@ -1097,10 +1095,11 @@ class MainShell(cmd.Cmd):
1097
1095
  parser = argparse.ArgumentParser(prog='create_config_file', add_help=True)
1098
1096
  parser.add_argument("-fn", "--file_name", help="file name for your config file", default="launchpy_config.json",type=str)
1099
1097
  parser.add_argument("-m","--multi_org", help="Enable multi-org configuration", action="store_true", default=False)
1098
+ parser.add_argument("-p","--proxy", help="Enable proxy for the connection", action="store_true", default=False)
1100
1099
  try:
1101
1100
  args = parser.parse_args(shlex.split(arg))
1102
1101
  filename = args.file_name
1103
- launchpy.createConfigFile(filename=filename,multi_org=args.multi_org)
1102
+ launchpy.createConfigFile(filename=filename,proxy=args.proxy,multi_org=args.multi_org)
1104
1103
  filename_json = filename + ".json" if not filename.endswith(".json") else filename
1105
1104
  console.print(f"Configuration file created at {Path.cwd() / Path(filename_json)}", style="green")
1106
1105
  return
@@ -1121,8 +1120,10 @@ class MainShell(cmd.Cmd):
1121
1120
  parser.add_argument("-cf", "--config_file", help="Path to config file", default=None)
1122
1121
  parser.add_argument("-p", "--property", help="Property Name to auto-load on startup", default=None)
1123
1122
  parser.add_argument("-on", "--org_name", help="Organization Name to use from config file", default=None)
1123
+ parser.add_argument("--proxy", help="Proxy URL for both HTTP and HTTPS")
1124
1124
  try:
1125
1125
  args = parser.parse_args(shlex.split(arg))
1126
+ proxy = _validate_proxy(args.proxy)
1126
1127
  if args.config_file is not None:
1127
1128
  config_path = Path(args.config_file)
1128
1129
  if not config_path.is_absolute():
@@ -1136,6 +1137,7 @@ class MainShell(cmd.Cmd):
1136
1137
  self.config[config_key]['org_id'] = dict_config.get("org_id", args.org_id)
1137
1138
  self.config[config_key]['client_id'] = dict_config.get("client_id", dict_config.get("api_key", args.client_id))
1138
1139
  self.config[config_key]['scopes'] = dict_config.get("scopes", args.scopes)
1140
+ self.config[config_key]['proxy'] = proxy if proxy is not None else _validate_proxy(dict_config.get("proxy"))
1139
1141
  config_objects.update(self.config)
1140
1142
  elif type(dict_config) == list:
1141
1143
  self.config = {}
@@ -1149,6 +1151,7 @@ class MainShell(cmd.Cmd):
1149
1151
  self.config[config_key]['org_id'] = config.get("org_id", args.org_id)
1150
1152
  self.config[config_key]['client_id'] = config.get("client_id", config.get("api_key", args.client_id))
1151
1153
  self.config[config_key]['scopes'] = config.get("scopes", args.scopes)
1154
+ self.config[config_key]['proxy'] = proxy if proxy is not None else _validate_proxy(config.get("proxy"))
1152
1155
  config_objects.update(self.config)
1153
1156
  config_key = dict_config[0].get("org_name")
1154
1157
  else:
@@ -1160,6 +1163,7 @@ class MainShell(cmd.Cmd):
1160
1163
  if args.org_id: self.config[config_key]['org_id'] = str(args.org_id)
1161
1164
  if args.scopes: self.config[config_key]['scopes'] = str(args.scopes)
1162
1165
  if args.client_id: self.config[config_key]['client_id'] = str(args.client_id)
1166
+ self.config[config_key]['proxy'] = proxy
1163
1167
  config_objects.update(self.config)
1164
1168
  missing = [
1165
1169
  key for key, value in {
@@ -1423,6 +1427,7 @@ def main():
1423
1427
  parser.add_argument("-cf", "--config_file", help="Path to config file", default=None)
1424
1428
  parser.add_argument("-p", "--property", help="Property Name to auto-load on startup", default=None)
1425
1429
  parser.add_argument("-on", "--org_name", help="Organization Name to use from config file", default=None)
1430
+ parser.add_argument("--proxy", help="Proxy URL for both HTTP and HTTPS")
1426
1431
  args = parser.parse_args()
1427
1432
  shell = MainShell(**vars(args))
1428
1433
  try: