omegaconf-pydevd 2.4.0.dev10__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.
- omegaconf_pydevd-2.4.0.dev10/LICENSE +29 -0
- omegaconf_pydevd-2.4.0.dev10/MANIFEST.in +4 -0
- omegaconf_pydevd-2.4.0.dev10/PKG-INFO +92 -0
- omegaconf_pydevd-2.4.0.dev10/README.md +61 -0
- omegaconf_pydevd-2.4.0.dev10/omegaconf_pydevd.egg-info/PKG-INFO +92 -0
- omegaconf_pydevd-2.4.0.dev10/omegaconf_pydevd.egg-info/SOURCES.txt +16 -0
- omegaconf_pydevd-2.4.0.dev10/omegaconf_pydevd.egg-info/dependency_links.txt +1 -0
- omegaconf_pydevd-2.4.0.dev10/omegaconf_pydevd.egg-info/namespace_packages.txt +2 -0
- omegaconf_pydevd-2.4.0.dev10/omegaconf_pydevd.egg-info/requires.txt +1 -0
- omegaconf_pydevd-2.4.0.dev10/omegaconf_pydevd.egg-info/top_level.txt +1 -0
- omegaconf_pydevd-2.4.0.dev10/pydevd_plugins/__init__.py +6 -0
- omegaconf_pydevd-2.4.0.dev10/pydevd_plugins/extensions/__init__.py +6 -0
- omegaconf_pydevd-2.4.0.dev10/pydevd_plugins/extensions/pydevd_plugin_omegaconf.py +126 -0
- omegaconf_pydevd-2.4.0.dev10/setup.cfg +4 -0
- omegaconf_pydevd-2.4.0.dev10/setup.py +53 -0
- omegaconf_pydevd-2.4.0.dev10/tests/test_pydev_resolver_plugin.py +268 -0
- omegaconf_pydevd-2.4.0.dev10/tests/test_pydevd_plugin_namespace.py +240 -0
- omegaconf_pydevd-2.4.0.dev10/version.py +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018, Omry Yadan
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: omegaconf-pydevd
|
|
3
|
+
Version: 2.4.0.dev10
|
|
4
|
+
Summary: pydevd debugger plugin for OmegaConf
|
|
5
|
+
Home-page: https://github.com/omry/omegaconf
|
|
6
|
+
Author: Omry Yadan
|
|
7
|
+
Author-email: omry@yadan.net
|
|
8
|
+
Keywords: omegaconf pydevd debugpy debugger
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: omegaconf==2.4.0.dev10
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: keywords
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
Dynamic: summary
|
|
31
|
+
|
|
32
|
+
# OmegaConf pydevd Plugin
|
|
33
|
+
|
|
34
|
+
`omegaconf-pydevd` provides the optional `pydevd` plugin for OmegaConf objects.
|
|
35
|
+
|
|
36
|
+
This debugger integration is packaged separately from the main `omegaconf`
|
|
37
|
+
distribution so that the global `pydevd_plugins` namespace is only installed on
|
|
38
|
+
systems that explicitly opt into it.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install omegaconf-pydevd
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Debugger Demo
|
|
47
|
+
|
|
48
|
+
Use [examples/debug_demo.py](./examples/debug_demo.py) to inspect OmegaConf
|
|
49
|
+
objects in the debugger with `omegaconf-pydevd` installed.
|
|
50
|
+
|
|
51
|
+
Set a breakpoint on `print(cfg)` and inspect:
|
|
52
|
+
|
|
53
|
+
- `cfg`
|
|
54
|
+
- `cfg.greeting`
|
|
55
|
+
- `cfg.subprojects`
|
|
56
|
+
- `cfg.project`
|
|
57
|
+
|
|
58
|
+
With `omegaconf-pydevd` installed:
|
|
59
|
+
|
|
60
|
+
- missing fields render as debugger values instead of surfacing a debugger-time
|
|
61
|
+
exception while inspecting them
|
|
62
|
+
- interpolations are shown more clearly, including their resolved values
|
|
63
|
+
|
|
64
|
+
### Example Rendering
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
## Resolver Mode
|
|
69
|
+
|
|
70
|
+
Use the `OC_PYDEVD_RESOLVER` environment variable to select which resolver to
|
|
71
|
+
install:
|
|
72
|
+
|
|
73
|
+
- `USER`: default behavior, useful when debugging OmegaConf objects
|
|
74
|
+
- `DEV`: useful when debugging OmegaConf itself and inspecting its internal
|
|
75
|
+
data model
|
|
76
|
+
- `DISABLE`: disable the OmegaConf resolver
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
OC_PYDEVD_RESOLVER=DEV python your_program.py
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Development
|
|
85
|
+
|
|
86
|
+
From this repository root:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
cd subprojects/omegaconf-pydevd
|
|
90
|
+
pip install -r ../../requirements/dev.txt -e .
|
|
91
|
+
pytest
|
|
92
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# OmegaConf pydevd Plugin
|
|
2
|
+
|
|
3
|
+
`omegaconf-pydevd` provides the optional `pydevd` plugin for OmegaConf objects.
|
|
4
|
+
|
|
5
|
+
This debugger integration is packaged separately from the main `omegaconf`
|
|
6
|
+
distribution so that the global `pydevd_plugins` namespace is only installed on
|
|
7
|
+
systems that explicitly opt into it.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install omegaconf-pydevd
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Debugger Demo
|
|
16
|
+
|
|
17
|
+
Use [examples/debug_demo.py](./examples/debug_demo.py) to inspect OmegaConf
|
|
18
|
+
objects in the debugger with `omegaconf-pydevd` installed.
|
|
19
|
+
|
|
20
|
+
Set a breakpoint on `print(cfg)` and inspect:
|
|
21
|
+
|
|
22
|
+
- `cfg`
|
|
23
|
+
- `cfg.greeting`
|
|
24
|
+
- `cfg.subprojects`
|
|
25
|
+
- `cfg.project`
|
|
26
|
+
|
|
27
|
+
With `omegaconf-pydevd` installed:
|
|
28
|
+
|
|
29
|
+
- missing fields render as debugger values instead of surfacing a debugger-time
|
|
30
|
+
exception while inspecting them
|
|
31
|
+
- interpolations are shown more clearly, including their resolved values
|
|
32
|
+
|
|
33
|
+
### Example Rendering
|
|
34
|
+
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
## Resolver Mode
|
|
38
|
+
|
|
39
|
+
Use the `OC_PYDEVD_RESOLVER` environment variable to select which resolver to
|
|
40
|
+
install:
|
|
41
|
+
|
|
42
|
+
- `USER`: default behavior, useful when debugging OmegaConf objects
|
|
43
|
+
- `DEV`: useful when debugging OmegaConf itself and inspecting its internal
|
|
44
|
+
data model
|
|
45
|
+
- `DISABLE`: disable the OmegaConf resolver
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
OC_PYDEVD_RESOLVER=DEV python your_program.py
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
From this repository root:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd subprojects/omegaconf-pydevd
|
|
59
|
+
pip install -r ../../requirements/dev.txt -e .
|
|
60
|
+
pytest
|
|
61
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: omegaconf-pydevd
|
|
3
|
+
Version: 2.4.0.dev10
|
|
4
|
+
Summary: pydevd debugger plugin for OmegaConf
|
|
5
|
+
Home-page: https://github.com/omry/omegaconf
|
|
6
|
+
Author: Omry Yadan
|
|
7
|
+
Author-email: omry@yadan.net
|
|
8
|
+
Keywords: omegaconf pydevd debugpy debugger
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: omegaconf==2.4.0.dev10
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: keywords
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
Dynamic: summary
|
|
31
|
+
|
|
32
|
+
# OmegaConf pydevd Plugin
|
|
33
|
+
|
|
34
|
+
`omegaconf-pydevd` provides the optional `pydevd` plugin for OmegaConf objects.
|
|
35
|
+
|
|
36
|
+
This debugger integration is packaged separately from the main `omegaconf`
|
|
37
|
+
distribution so that the global `pydevd_plugins` namespace is only installed on
|
|
38
|
+
systems that explicitly opt into it.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install omegaconf-pydevd
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Debugger Demo
|
|
47
|
+
|
|
48
|
+
Use [examples/debug_demo.py](./examples/debug_demo.py) to inspect OmegaConf
|
|
49
|
+
objects in the debugger with `omegaconf-pydevd` installed.
|
|
50
|
+
|
|
51
|
+
Set a breakpoint on `print(cfg)` and inspect:
|
|
52
|
+
|
|
53
|
+
- `cfg`
|
|
54
|
+
- `cfg.greeting`
|
|
55
|
+
- `cfg.subprojects`
|
|
56
|
+
- `cfg.project`
|
|
57
|
+
|
|
58
|
+
With `omegaconf-pydevd` installed:
|
|
59
|
+
|
|
60
|
+
- missing fields render as debugger values instead of surfacing a debugger-time
|
|
61
|
+
exception while inspecting them
|
|
62
|
+
- interpolations are shown more clearly, including their resolved values
|
|
63
|
+
|
|
64
|
+
### Example Rendering
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
## Resolver Mode
|
|
69
|
+
|
|
70
|
+
Use the `OC_PYDEVD_RESOLVER` environment variable to select which resolver to
|
|
71
|
+
install:
|
|
72
|
+
|
|
73
|
+
- `USER`: default behavior, useful when debugging OmegaConf objects
|
|
74
|
+
- `DEV`: useful when debugging OmegaConf itself and inspecting its internal
|
|
75
|
+
data model
|
|
76
|
+
- `DISABLE`: disable the OmegaConf resolver
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
OC_PYDEVD_RESOLVER=DEV python your_program.py
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Development
|
|
85
|
+
|
|
86
|
+
From this repository root:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
cd subprojects/omegaconf-pydevd
|
|
90
|
+
pip install -r ../../requirements/dev.txt -e .
|
|
91
|
+
pytest
|
|
92
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
setup.py
|
|
5
|
+
version.py
|
|
6
|
+
omegaconf_pydevd.egg-info/PKG-INFO
|
|
7
|
+
omegaconf_pydevd.egg-info/SOURCES.txt
|
|
8
|
+
omegaconf_pydevd.egg-info/dependency_links.txt
|
|
9
|
+
omegaconf_pydevd.egg-info/namespace_packages.txt
|
|
10
|
+
omegaconf_pydevd.egg-info/requires.txt
|
|
11
|
+
omegaconf_pydevd.egg-info/top_level.txt
|
|
12
|
+
pydevd_plugins/__init__.py
|
|
13
|
+
pydevd_plugins/extensions/__init__.py
|
|
14
|
+
pydevd_plugins/extensions/pydevd_plugin_omegaconf.py
|
|
15
|
+
tests/test_pydev_resolver_plugin.py
|
|
16
|
+
tests/test_pydevd_plugin_namespace.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
omegaconf==2.4.0.dev10
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pydevd_plugins
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# based on https://github.com/fabioz/PyDev.Debugger/tree/main/pydevd_plugins/extensions
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
from _pydevd_bundle.pydevd_extension_api import ( # type: ignore
|
|
7
|
+
StrPresentationProvider,
|
|
8
|
+
TypeResolveProvider,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
DEBUG = False
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def print_debug(msg: str) -> None: # pragma: no cover
|
|
15
|
+
if DEBUG:
|
|
16
|
+
print(msg)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def find_mod_attr(mod_name: str, attr: str) -> Any:
|
|
20
|
+
mod = sys.modules.get(mod_name)
|
|
21
|
+
return getattr(mod, attr, None)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class OmegaConfDeveloperResolver(object):
|
|
25
|
+
def can_provide(self, type_object: Any, type_name: str) -> bool:
|
|
26
|
+
Node = find_mod_attr("omegaconf", "Node")
|
|
27
|
+
return Node is not None and issubclass(type_object, Node)
|
|
28
|
+
|
|
29
|
+
def resolve(self, obj: Any, attribute: str) -> Any:
|
|
30
|
+
return getattr(obj, attribute)
|
|
31
|
+
|
|
32
|
+
def get_dictionary(self, obj: Any) -> Any:
|
|
33
|
+
return obj.__dict__
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class OmegaConfUserResolver(StrPresentationProvider): # type: ignore
|
|
37
|
+
def __init__(self) -> None:
|
|
38
|
+
self.Node = find_mod_attr("omegaconf", "Node")
|
|
39
|
+
self.ValueNode = find_mod_attr("omegaconf", "ValueNode")
|
|
40
|
+
self.ListConfig = find_mod_attr("omegaconf", "ListConfig")
|
|
41
|
+
self.DictConfig = find_mod_attr("omegaconf", "DictConfig")
|
|
42
|
+
self.InterpolationResolutionError = find_mod_attr(
|
|
43
|
+
"omegaconf.errors", "InterpolationResolutionError"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def can_provide(self, type_object: Any, type_name: str) -> bool:
|
|
47
|
+
return self.Node is not None and issubclass(type_object, self.Node)
|
|
48
|
+
|
|
49
|
+
def resolve(self, obj: Any, attribute: Any) -> Any:
|
|
50
|
+
if isinstance(obj, self.ListConfig) and isinstance(attribute, str):
|
|
51
|
+
attribute = int(attribute)
|
|
52
|
+
|
|
53
|
+
if isinstance(obj, self.Node):
|
|
54
|
+
obj = obj._dereference_node()
|
|
55
|
+
|
|
56
|
+
val = obj.__dict__["_content"][attribute]
|
|
57
|
+
|
|
58
|
+
print_debug(
|
|
59
|
+
f"resolving {obj} ({type(obj).__name__}), {attribute} -> {val} ({type(val).__name__})"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return val
|
|
63
|
+
|
|
64
|
+
def _is_simple_value(self, val: Any) -> bool:
|
|
65
|
+
return (
|
|
66
|
+
isinstance(val, self.ValueNode)
|
|
67
|
+
and not val._is_none()
|
|
68
|
+
and not val._is_missing()
|
|
69
|
+
and not val._is_interpolation()
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def get_dictionary(self, obj: Any) -> Dict[str, Any]:
|
|
73
|
+
d = self._get_dictionary(obj)
|
|
74
|
+
print_debug(f"get_dictionary {obj}, ({type(obj).__name__}) -> {d}")
|
|
75
|
+
return d
|
|
76
|
+
|
|
77
|
+
def _get_dictionary(self, obj: Any) -> Dict[str, Any]:
|
|
78
|
+
if isinstance(obj, self.Node):
|
|
79
|
+
obj = obj._maybe_dereference_node()
|
|
80
|
+
if obj is None or obj._is_none() or obj._is_missing():
|
|
81
|
+
return {}
|
|
82
|
+
|
|
83
|
+
if isinstance(obj, self.DictConfig):
|
|
84
|
+
d = {}
|
|
85
|
+
for k, v in obj.__dict__["_content"].items():
|
|
86
|
+
if self._is_simple_value(v):
|
|
87
|
+
v = v._value()
|
|
88
|
+
d[k] = v
|
|
89
|
+
elif isinstance(obj, self.ListConfig):
|
|
90
|
+
d = {}
|
|
91
|
+
for idx, v in enumerate(obj.__dict__["_content"]):
|
|
92
|
+
if self._is_simple_value(v):
|
|
93
|
+
v = v._value()
|
|
94
|
+
d[str(idx)] = v
|
|
95
|
+
else:
|
|
96
|
+
d = {}
|
|
97
|
+
|
|
98
|
+
return d
|
|
99
|
+
|
|
100
|
+
def get_str(self, val: Any) -> str:
|
|
101
|
+
if val._is_missing():
|
|
102
|
+
return "??? <MISSING>"
|
|
103
|
+
if val._is_interpolation():
|
|
104
|
+
try:
|
|
105
|
+
dr = val._dereference_node()
|
|
106
|
+
except self.InterpolationResolutionError as e:
|
|
107
|
+
dr = f"ERR: {e}"
|
|
108
|
+
return f"{val._value()} -> {dr}"
|
|
109
|
+
else:
|
|
110
|
+
return f"{val}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# OC_PYDEVD_RESOLVER env can take:
|
|
114
|
+
# DISABLE: Do not install a pydevd resolver
|
|
115
|
+
# USER: Install a resolver for OmegaConf users (default)
|
|
116
|
+
# DEV: Install a resolver for OmegaConf developers. Shows underlying data-model in the debugger.
|
|
117
|
+
resolver = os.environ.get("OC_PYDEVD_RESOLVER", "USER").upper()
|
|
118
|
+
if resolver != "DISABLE": # pragma: no cover
|
|
119
|
+
if resolver == "USER":
|
|
120
|
+
TypeResolveProvider.register(OmegaConfUserResolver)
|
|
121
|
+
elif resolver == "DEV":
|
|
122
|
+
TypeResolveProvider.register(OmegaConfDeveloperResolver)
|
|
123
|
+
else:
|
|
124
|
+
sys.stderr.write(
|
|
125
|
+
f"OmegaConf pydev plugin: Not installing. Unknown mode {resolver}. Supported one of [USER, DEV, DISABLE]\n"
|
|
126
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import pathlib
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
import setuptools
|
|
6
|
+
|
|
7
|
+
ROOT = pathlib.Path(__file__).parent.resolve()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find_version(*file_paths: str) -> str:
|
|
11
|
+
with open(ROOT / pathlib.Path(*file_paths), "r", encoding="utf-8") as fp:
|
|
12
|
+
version_file = fp.read()
|
|
13
|
+
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
|
|
14
|
+
if version_match:
|
|
15
|
+
return version_match.group(1)
|
|
16
|
+
raise RuntimeError("Unable to find version string.")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
VERSION = find_version("version.py")
|
|
20
|
+
|
|
21
|
+
with (ROOT / "README.md").open("r", encoding="utf-8") as fh:
|
|
22
|
+
LONG_DESC = fh.read()
|
|
23
|
+
|
|
24
|
+
setuptools.setup(
|
|
25
|
+
name="omegaconf-pydevd",
|
|
26
|
+
version=VERSION,
|
|
27
|
+
author="Omry Yadan",
|
|
28
|
+
author_email="omry@yadan.net",
|
|
29
|
+
description="pydevd debugger plugin for OmegaConf",
|
|
30
|
+
long_description=LONG_DESC,
|
|
31
|
+
long_description_content_type="text/markdown",
|
|
32
|
+
url="https://github.com/omry/omegaconf",
|
|
33
|
+
keywords="omegaconf pydevd debugpy debugger",
|
|
34
|
+
packages=[
|
|
35
|
+
"pydevd_plugins",
|
|
36
|
+
"pydevd_plugins.extensions",
|
|
37
|
+
],
|
|
38
|
+
namespace_packages=[
|
|
39
|
+
"pydevd_plugins",
|
|
40
|
+
"pydevd_plugins.extensions",
|
|
41
|
+
],
|
|
42
|
+
python_requires=">=3.10",
|
|
43
|
+
classifiers=[
|
|
44
|
+
"Programming Language :: Python :: 3.10",
|
|
45
|
+
"Programming Language :: Python :: 3.11",
|
|
46
|
+
"Programming Language :: Python :: 3.12",
|
|
47
|
+
"Programming Language :: Python :: 3.13",
|
|
48
|
+
"Programming Language :: Python :: 3.14",
|
|
49
|
+
"License :: OSI Approved :: BSD License",
|
|
50
|
+
"Operating System :: OS Independent",
|
|
51
|
+
],
|
|
52
|
+
install_requires=[f"omegaconf=={VERSION}"],
|
|
53
|
+
)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import builtins
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydevd_plugins.extensions.pydevd_plugin_omegaconf import (
|
|
6
|
+
OmegaConfDeveloperResolver,
|
|
7
|
+
OmegaConfUserResolver,
|
|
8
|
+
)
|
|
9
|
+
from pytest import fixture, mark, param
|
|
10
|
+
|
|
11
|
+
from omegaconf import (
|
|
12
|
+
AnyNode,
|
|
13
|
+
BooleanNode,
|
|
14
|
+
BytesNode,
|
|
15
|
+
Container,
|
|
16
|
+
DictConfig,
|
|
17
|
+
EnumNode,
|
|
18
|
+
FloatNode,
|
|
19
|
+
IntegerNode,
|
|
20
|
+
ListConfig,
|
|
21
|
+
Node,
|
|
22
|
+
OmegaConf,
|
|
23
|
+
PathNode,
|
|
24
|
+
StringNode,
|
|
25
|
+
ValueNode,
|
|
26
|
+
)
|
|
27
|
+
from omegaconf._utils import type_str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Color(Enum):
|
|
31
|
+
RED = 1
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@fixture
|
|
35
|
+
def resolver() -> Any:
|
|
36
|
+
yield OmegaConfUserResolver()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@mark.parametrize(
|
|
40
|
+
("obj", "expected"),
|
|
41
|
+
[
|
|
42
|
+
param(AnyNode(10), {}, id="any:10"),
|
|
43
|
+
param(StringNode("foo"), {}, id="str:foo"),
|
|
44
|
+
param(IntegerNode(10), {}, id="int:10"),
|
|
45
|
+
param(FloatNode(3.14), {}, id="float:3.14"),
|
|
46
|
+
param(BooleanNode(True), {}, id="bool:True"),
|
|
47
|
+
param(BytesNode(b"binary"), {}, id="bytes:binary"),
|
|
48
|
+
param(PathNode("hello.txt"), {}, id="path:hello.txt"),
|
|
49
|
+
param(EnumNode(enum_type=Color, value=Color.RED), {}, id="Color:Color.RED"),
|
|
50
|
+
param(AnyNode("${foo}", parent=DictConfig({"foo": 10})), {}, id="any:inter_10"),
|
|
51
|
+
param(DictConfig({"a": 10}), {"a": AnyNode(10)}, id="dict"),
|
|
52
|
+
param(
|
|
53
|
+
DictConfig({"a": 10, "b": "${a}"}),
|
|
54
|
+
{"a": AnyNode(10), "b": AnyNode("${a}")},
|
|
55
|
+
id="dict:interpolation_value",
|
|
56
|
+
),
|
|
57
|
+
param(
|
|
58
|
+
DictConfig({"a": 10, "b": "${zzz}"}),
|
|
59
|
+
{"a": AnyNode(10), "b": AnyNode("${zzz}")},
|
|
60
|
+
id="dict:interpolation_value_error",
|
|
61
|
+
),
|
|
62
|
+
param(
|
|
63
|
+
DictConfig({"a": 10, "b": "foo_${a}"}),
|
|
64
|
+
{"a": AnyNode(10), "b": AnyNode("foo_${a}")},
|
|
65
|
+
id="dict:str_interpolation_value",
|
|
66
|
+
),
|
|
67
|
+
param(DictConfig("${zzz}"), {}, id="dict:inter_error"),
|
|
68
|
+
param(
|
|
69
|
+
ListConfig(["a", "b"]), {"0": AnyNode("a"), "1": AnyNode("b")}, id="list"
|
|
70
|
+
),
|
|
71
|
+
param(
|
|
72
|
+
ListConfig(["${1}", 10]),
|
|
73
|
+
{"0": AnyNode("${1}"), "1": AnyNode(10)},
|
|
74
|
+
id="list:interpolation_value",
|
|
75
|
+
),
|
|
76
|
+
param(ListConfig("${zzz}"), {}, id="list:inter_error"),
|
|
77
|
+
],
|
|
78
|
+
)
|
|
79
|
+
def test_get_dictionary_node(resolver: Any, obj: Any, expected: Any) -> None:
|
|
80
|
+
res = resolver.get_dictionary(obj)
|
|
81
|
+
assert res == expected
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@mark.parametrize(
|
|
85
|
+
("obj", "attribute", "expected"),
|
|
86
|
+
[
|
|
87
|
+
param(DictConfig({"a": 10}), "a", AnyNode(10), id="dict"),
|
|
88
|
+
param(
|
|
89
|
+
DictConfig({"a": DictConfig(None)}),
|
|
90
|
+
"a",
|
|
91
|
+
DictConfig(None),
|
|
92
|
+
id="dict:none",
|
|
93
|
+
),
|
|
94
|
+
param(ListConfig([10]), 0, AnyNode(10), id="list"),
|
|
95
|
+
param(ListConfig(["???"]), 0, AnyNode("???"), id="list:missing_item"),
|
|
96
|
+
],
|
|
97
|
+
)
|
|
98
|
+
def test_resolve(
|
|
99
|
+
resolver: Any,
|
|
100
|
+
obj: Any,
|
|
101
|
+
attribute: str,
|
|
102
|
+
expected: Any,
|
|
103
|
+
) -> None:
|
|
104
|
+
res = resolver.resolve(obj, attribute)
|
|
105
|
+
assert res == expected
|
|
106
|
+
assert type(res) is type(expected)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@mark.parametrize(
|
|
110
|
+
("obj", "attribute", "expected"),
|
|
111
|
+
[
|
|
112
|
+
param(
|
|
113
|
+
OmegaConf.create({"a": 10, "inter": "${a}"}), "inter", {}, id="dict:inter"
|
|
114
|
+
),
|
|
115
|
+
param(
|
|
116
|
+
OmegaConf.create({"missing": "???"}),
|
|
117
|
+
"missing",
|
|
118
|
+
{},
|
|
119
|
+
id="dict:missing_value",
|
|
120
|
+
),
|
|
121
|
+
param(OmegaConf.create({"none": None}), "none", {}, id="dict:none_value"),
|
|
122
|
+
param(
|
|
123
|
+
OmegaConf.create({"none": DictConfig(None)}),
|
|
124
|
+
"none",
|
|
125
|
+
{},
|
|
126
|
+
id="dict:none_dictconfig_value",
|
|
127
|
+
),
|
|
128
|
+
param(
|
|
129
|
+
OmegaConf.create({"missing": DictConfig("???")}),
|
|
130
|
+
"missing",
|
|
131
|
+
{},
|
|
132
|
+
id="dict:missing_dictconfig_value",
|
|
133
|
+
),
|
|
134
|
+
param(
|
|
135
|
+
OmegaConf.create({"a": {"b": 10}, "b": DictConfig("${a}")}),
|
|
136
|
+
"b",
|
|
137
|
+
{"b": 10},
|
|
138
|
+
id="dict:interpolation_dictconfig_value",
|
|
139
|
+
),
|
|
140
|
+
],
|
|
141
|
+
)
|
|
142
|
+
def test_get_dictionary_dictconfig(
|
|
143
|
+
resolver: Any,
|
|
144
|
+
obj: Any,
|
|
145
|
+
attribute: str,
|
|
146
|
+
expected: Any,
|
|
147
|
+
) -> None:
|
|
148
|
+
field = resolver.resolve(obj, attribute)
|
|
149
|
+
res = resolver.get_dictionary(field)
|
|
150
|
+
assert res == expected
|
|
151
|
+
assert type(res) is type(expected)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@mark.parametrize(
|
|
155
|
+
("obj", "attribute", "expected"),
|
|
156
|
+
[
|
|
157
|
+
param(
|
|
158
|
+
ListConfig("${list}", parent=DictConfig({"list": [{"a": 10}]})),
|
|
159
|
+
"0",
|
|
160
|
+
{"a": 10},
|
|
161
|
+
id="inter_list:dict_element",
|
|
162
|
+
),
|
|
163
|
+
param(
|
|
164
|
+
DictConfig("${dict}", parent=DictConfig({"dict": {"a": {"b": 10}}})),
|
|
165
|
+
"a",
|
|
166
|
+
{"b": 10},
|
|
167
|
+
id="inter_dict:dict_element",
|
|
168
|
+
),
|
|
169
|
+
],
|
|
170
|
+
)
|
|
171
|
+
def test_resolve_through_container_interpolation(
|
|
172
|
+
resolver: Any, obj: Any, attribute: str, expected: Any
|
|
173
|
+
) -> None:
|
|
174
|
+
res = resolver.resolve(obj, attribute)
|
|
175
|
+
assert res == expected
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@mark.parametrize(
|
|
179
|
+
("obj", "attribute", "expected"),
|
|
180
|
+
[
|
|
181
|
+
param(OmegaConf.create(["${.1}", 10]), "0", {}, id="list:inter_value"),
|
|
182
|
+
param(
|
|
183
|
+
OmegaConf.create({"a": ListConfig(None)}),
|
|
184
|
+
"a",
|
|
185
|
+
{},
|
|
186
|
+
id="list:none_listconfig_value",
|
|
187
|
+
),
|
|
188
|
+
param(
|
|
189
|
+
OmegaConf.create({"a": ListConfig("???")}),
|
|
190
|
+
"a",
|
|
191
|
+
{},
|
|
192
|
+
id="list:missing_listconfig_value",
|
|
193
|
+
),
|
|
194
|
+
param(
|
|
195
|
+
OmegaConf.create({"a": [1, 2], "b": ListConfig("${a}")}),
|
|
196
|
+
"b",
|
|
197
|
+
{"0": 1, "1": 2},
|
|
198
|
+
id="list:interpolation_listconfig_value",
|
|
199
|
+
),
|
|
200
|
+
],
|
|
201
|
+
)
|
|
202
|
+
def test_get_dictionary_listconfig(
|
|
203
|
+
resolver: Any,
|
|
204
|
+
obj: Any,
|
|
205
|
+
attribute: str,
|
|
206
|
+
expected: Any,
|
|
207
|
+
) -> None:
|
|
208
|
+
field = resolver.resolve(obj, attribute)
|
|
209
|
+
res = resolver.get_dictionary(field)
|
|
210
|
+
assert res == expected
|
|
211
|
+
assert type(res) is type(expected)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@mark.parametrize("resolver", [OmegaConfUserResolver(), OmegaConfDeveloperResolver()])
|
|
215
|
+
@mark.parametrize(
|
|
216
|
+
("type_", "expected"),
|
|
217
|
+
[
|
|
218
|
+
(Container, True),
|
|
219
|
+
(DictConfig, True),
|
|
220
|
+
(ListConfig, True),
|
|
221
|
+
(Node, True),
|
|
222
|
+
(ValueNode, True),
|
|
223
|
+
(AnyNode, True),
|
|
224
|
+
(IntegerNode, True),
|
|
225
|
+
(FloatNode, True),
|
|
226
|
+
(StringNode, True),
|
|
227
|
+
(BooleanNode, True),
|
|
228
|
+
(BytesNode, True),
|
|
229
|
+
(PathNode, True),
|
|
230
|
+
(EnumNode, True),
|
|
231
|
+
(builtins.int, False),
|
|
232
|
+
(dict, False),
|
|
233
|
+
(list, False),
|
|
234
|
+
],
|
|
235
|
+
)
|
|
236
|
+
def test_can_provide(resolver: Any, type_: Any, expected: bool) -> None:
|
|
237
|
+
assert resolver.can_provide(type_, type_str(type_)) == expected
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@mark.parametrize(
|
|
241
|
+
("obj", "expected"),
|
|
242
|
+
[
|
|
243
|
+
(AnyNode(10), "10"),
|
|
244
|
+
(AnyNode("???"), "??? <MISSING>"),
|
|
245
|
+
(
|
|
246
|
+
AnyNode("${foo}", parent=OmegaConf.create({})),
|
|
247
|
+
"${foo} -> ERR: Interpolation key 'foo' not found",
|
|
248
|
+
),
|
|
249
|
+
(AnyNode("${foo}", parent=OmegaConf.create({"foo": 10})), "${foo} -> 10"),
|
|
250
|
+
(
|
|
251
|
+
DictConfig("${foo}", parent=OmegaConf.create({"foo": {"a": 10}})),
|
|
252
|
+
"${foo} -> {'a': 10}",
|
|
253
|
+
),
|
|
254
|
+
(
|
|
255
|
+
ListConfig("${foo}", parent=OmegaConf.create({"foo": [1, 2]})),
|
|
256
|
+
"${foo} -> [1, 2]",
|
|
257
|
+
),
|
|
258
|
+
],
|
|
259
|
+
)
|
|
260
|
+
def test_get_str(resolver: Any, obj: Any, expected: str) -> None:
|
|
261
|
+
assert resolver.get_str(obj) == expected
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def test_dev_resolver() -> None:
|
|
265
|
+
resolver = OmegaConfDeveloperResolver()
|
|
266
|
+
cfg = OmegaConf.create({"foo": 10})
|
|
267
|
+
assert resolver.resolve(cfg, "_metadata") is cfg.__dict__["_metadata"]
|
|
268
|
+
assert resolver.get_dictionary(cfg) is cfg.__dict__
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
OLD_NAMESPACE_INIT = """import pkgutil
|
|
8
|
+
|
|
9
|
+
__path__ = pkgutil.extend_path(__path__, __name__)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
FAKE_PKG_RESOURCES = """_namespaces = set()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _package_paths(name):
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
|
|
20
|
+
relpath = os.path.join(*name.split("."))
|
|
21
|
+
paths = []
|
|
22
|
+
for entry in sys.path:
|
|
23
|
+
if not entry:
|
|
24
|
+
continue
|
|
25
|
+
candidate = os.path.join(entry, relpath)
|
|
26
|
+
if os.path.isdir(candidate):
|
|
27
|
+
paths.append(candidate)
|
|
28
|
+
return paths
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def declare_namespace(name):
|
|
32
|
+
import sys
|
|
33
|
+
|
|
34
|
+
_namespaces.add(name)
|
|
35
|
+
sys.modules[name].__path__ = _package_paths(name)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def fixup_namespace_packages(_):
|
|
39
|
+
import sys
|
|
40
|
+
|
|
41
|
+
for name in _namespaces:
|
|
42
|
+
module = sys.modules.get(name)
|
|
43
|
+
if module is not None:
|
|
44
|
+
module.__path__ = _package_paths(name)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
DISCOVERY_SCRIPT = """import importlib
|
|
49
|
+
import json
|
|
50
|
+
import os
|
|
51
|
+
import pkgutil
|
|
52
|
+
import sys
|
|
53
|
+
|
|
54
|
+
import pydevd_plugins.extensions as extensions
|
|
55
|
+
import pkg_resources
|
|
56
|
+
|
|
57
|
+
plugin_root = os.environ["OMEGACONF_PYDEVD_PLUGIN_ROOT"]
|
|
58
|
+
sys.path.insert(0, plugin_root)
|
|
59
|
+
importlib.invalidate_caches()
|
|
60
|
+
pkg_resources.fixup_namespace_packages(plugin_root)
|
|
61
|
+
|
|
62
|
+
print(
|
|
63
|
+
json.dumps(
|
|
64
|
+
sorted(
|
|
65
|
+
name
|
|
66
|
+
for _, name, _ in pkgutil.walk_packages(
|
|
67
|
+
extensions.__path__, extensions.__name__ + "."
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
EDITABLE_DISCOVERY_SCRIPT = """import importlib
|
|
76
|
+
import json
|
|
77
|
+
import os
|
|
78
|
+
import pkgutil
|
|
79
|
+
import sys
|
|
80
|
+
from importlib.machinery import ModuleSpec, PathFinder
|
|
81
|
+
from importlib.util import spec_from_file_location
|
|
82
|
+
from pathlib import Path
|
|
83
|
+
|
|
84
|
+
import pkg_resources
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class EditableFinder:
|
|
88
|
+
@classmethod
|
|
89
|
+
def find_spec(cls, fullname, path=None, target=None):
|
|
90
|
+
plugin_root = Path(os.environ["OMEGACONF_PYDEVD_PLUGIN_ROOT"])
|
|
91
|
+
mapping = {
|
|
92
|
+
"pydevd_plugins": plugin_root / "pydevd_plugins",
|
|
93
|
+
"pydevd_plugins.extensions": plugin_root / "pydevd_plugins" / "extensions",
|
|
94
|
+
}
|
|
95
|
+
candidate = mapping.get(fullname)
|
|
96
|
+
if candidate is not None:
|
|
97
|
+
return spec_from_file_location(fullname, candidate / "__init__.py")
|
|
98
|
+
|
|
99
|
+
parent, _, _ = fullname.rpartition(".")
|
|
100
|
+
if parent in mapping:
|
|
101
|
+
return PathFinder.find_spec(fullname, path=[str(mapping[parent])])
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def install_namespace_path(fullname, path_entry):
|
|
106
|
+
module = sys.modules.get(fullname)
|
|
107
|
+
if module is None:
|
|
108
|
+
module = importlib.import_module(fullname)
|
|
109
|
+
module_path = module.__dict__.setdefault("__path__", [])
|
|
110
|
+
if path_entry not in module_path:
|
|
111
|
+
module_path.append(path_entry)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
sys.meta_path.append(EditableFinder)
|
|
115
|
+
|
|
116
|
+
if os.environ.get("OMEGACONF_PYDEVD_INSTALL_NAMESPACE_PATHS") == "1":
|
|
117
|
+
plugin_root = Path(os.environ["OMEGACONF_PYDEVD_PLUGIN_ROOT"])
|
|
118
|
+
install_namespace_path("pydevd_plugins", str(plugin_root / "pydevd_plugins"))
|
|
119
|
+
install_namespace_path(
|
|
120
|
+
"pydevd_plugins.extensions",
|
|
121
|
+
str(plugin_root / "pydevd_plugins" / "extensions"),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
import pydevd_plugins.extensions as extensions
|
|
125
|
+
pkg_resources.fixup_namespace_packages(os.environ["OMEGACONF_PYDEVD_PLUGIN_ROOT"])
|
|
126
|
+
|
|
127
|
+
print(
|
|
128
|
+
json.dumps(
|
|
129
|
+
sorted(
|
|
130
|
+
name
|
|
131
|
+
for _, name, _ in pkgutil.walk_packages(
|
|
132
|
+
extensions.__path__, extensions.__name__ + "."
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _write_namespace_package(root: Path, init_text: str) -> None:
|
|
141
|
+
extensions_dir = root / "pydevd_plugins" / "extensions"
|
|
142
|
+
extensions_dir.mkdir(parents=True)
|
|
143
|
+
(root / "pydevd_plugins" / "__init__.py").write_text(init_text)
|
|
144
|
+
(extensions_dir / "__init__.py").write_text(init_text)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _build_project(tmp_path: Path, init_text: str) -> tuple[Path, Path, Path]:
|
|
148
|
+
project_root = tmp_path / "project"
|
|
149
|
+
support_root = project_root / "support"
|
|
150
|
+
bundled_root = project_root / "bundled"
|
|
151
|
+
plugin_root = project_root / "plugin"
|
|
152
|
+
|
|
153
|
+
support_root.mkdir(parents=True)
|
|
154
|
+
(support_root / "pkg_resources.py").write_text(FAKE_PKG_RESOURCES)
|
|
155
|
+
|
|
156
|
+
_write_namespace_package(bundled_root, init_text)
|
|
157
|
+
_write_namespace_package(plugin_root, init_text)
|
|
158
|
+
(
|
|
159
|
+
bundled_root / "pydevd_plugins" / "extensions" / "pydevd_plugin_builtin.py"
|
|
160
|
+
).write_text("X = 1\n")
|
|
161
|
+
(
|
|
162
|
+
plugin_root / "pydevd_plugins" / "extensions" / "pydevd_plugin_omegaconf.py"
|
|
163
|
+
).write_text("Y = 1\n")
|
|
164
|
+
|
|
165
|
+
return project_root, support_root, bundled_root
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _discover_extensions_from_editable_install(
|
|
169
|
+
tmp_path: Path,
|
|
170
|
+
*,
|
|
171
|
+
install_namespace_paths: bool,
|
|
172
|
+
) -> list[str]:
|
|
173
|
+
project_root, support_root, bundled_root = _build_project(tmp_path, OLD_NAMESPACE_INIT)
|
|
174
|
+
plugin_root = project_root / "plugin"
|
|
175
|
+
env = os.environ.copy()
|
|
176
|
+
env["PYTHONPATH"] = os.pathsep.join([str(support_root), str(bundled_root)])
|
|
177
|
+
env["OMEGACONF_PYDEVD_PLUGIN_ROOT"] = str(plugin_root)
|
|
178
|
+
if install_namespace_paths:
|
|
179
|
+
env["OMEGACONF_PYDEVD_INSTALL_NAMESPACE_PATHS"] = "1"
|
|
180
|
+
result = subprocess.run(
|
|
181
|
+
[sys.executable, "-S", "-c", EDITABLE_DISCOVERY_SCRIPT],
|
|
182
|
+
check=True,
|
|
183
|
+
capture_output=True,
|
|
184
|
+
cwd=project_root,
|
|
185
|
+
env=env,
|
|
186
|
+
text=True,
|
|
187
|
+
)
|
|
188
|
+
return json.loads(result.stdout)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _discover_extensions(tmp_path: Path, init_text: str) -> list[str]:
|
|
192
|
+
project_root, support_root, bundled_root = _build_project(tmp_path, init_text)
|
|
193
|
+
plugin_root = project_root / "plugin"
|
|
194
|
+
env = os.environ.copy()
|
|
195
|
+
env["PYTHONPATH"] = os.pathsep.join([str(support_root), str(bundled_root)])
|
|
196
|
+
env["OMEGACONF_PYDEVD_PLUGIN_ROOT"] = str(plugin_root)
|
|
197
|
+
result = subprocess.run(
|
|
198
|
+
[sys.executable, "-S", "-c", DISCOVERY_SCRIPT],
|
|
199
|
+
check=True,
|
|
200
|
+
capture_output=True,
|
|
201
|
+
cwd=project_root,
|
|
202
|
+
env=env,
|
|
203
|
+
text=True,
|
|
204
|
+
)
|
|
205
|
+
return json.loads(result.stdout)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_current_namespace_init_supports_late_plugin_discovery(tmp_path: Path) -> None:
|
|
209
|
+
init_text = (
|
|
210
|
+
Path(__file__).resolve().parents[1] / "pydevd_plugins" / "__init__.py"
|
|
211
|
+
).read_text()
|
|
212
|
+
discovered = _discover_extensions(tmp_path, init_text)
|
|
213
|
+
assert "pydevd_plugins.extensions.pydevd_plugin_omegaconf" in discovered
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_old_namespace_init_does_not_support_late_plugin_discovery(
|
|
217
|
+
tmp_path: Path,
|
|
218
|
+
) -> None:
|
|
219
|
+
discovered = _discover_extensions(tmp_path, OLD_NAMESPACE_INIT)
|
|
220
|
+
assert "pydevd_plugins.extensions.pydevd_plugin_omegaconf" not in discovered
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_editable_install_without_namespace_paths_is_not_discovered(
|
|
224
|
+
tmp_path: Path,
|
|
225
|
+
) -> None:
|
|
226
|
+
discovered = _discover_extensions_from_editable_install(
|
|
227
|
+
tmp_path,
|
|
228
|
+
install_namespace_paths=False,
|
|
229
|
+
)
|
|
230
|
+
assert "pydevd_plugins.extensions.pydevd_plugin_omegaconf" not in discovered
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def test_editable_install_with_namespace_paths_is_discovered(
|
|
234
|
+
tmp_path: Path,
|
|
235
|
+
) -> None:
|
|
236
|
+
discovered = _discover_extensions_from_editable_install(
|
|
237
|
+
tmp_path,
|
|
238
|
+
install_namespace_paths=True,
|
|
239
|
+
)
|
|
240
|
+
assert "pydevd_plugins.extensions.pydevd_plugin_omegaconf" in discovered
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.4.0.dev10"
|