Skip to content

Commit 9208c5e

Browse files
committed
Add example of using ProcessPoolExecutor to preserve namespaces
When switching namespaces there is no way to go back. Using process based executor allows running code inside namespaces without losing original ones. Add a documentation page with example as well as code under `examples` folder. Examples in `examples` folder are licensed under MIT.
1 parent 17d62da commit 9208c5e

File tree

7 files changed

+140
-0
lines changed

7 files changed

+140
-0
lines changed

LICENSES/MIT.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
MIT License
2+
3+
Copyright (c) <year> <copyright holders>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,5 @@ Current features implemented:
3434
## License
3535

3636
Python-lxns is licensed under Mozilla Public License Version 2.0.
37+
38+
Examples in `examples/` folder are licensed under MIT license.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ a different area of functionality:
1212
.. toctree::
1313

1414
namespace
15+
tips_and_tricks

docs/tips_and_tricks.rst

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
.. SPDX-License-Identifier: MPL-2.0
2+
.. SPDX-FileCopyrightText: 2024 igo95862
3+
4+
Tips and tricks
5+
===============
6+
7+
Using ProcessPoolExecutor to preserve current namespaces
8+
--------------------------------------------------------
9+
10+
When unsharing namespaces or switching to existing ones
11+
**there is no way to switch namespaces back**. The namespaces
12+
are process-wide so to preserve the current namespaces any
13+
concurrency methods that utilize independent processes can be used.
14+
15+
For example, ``ProcessPoolExecutor`` from the standard library's
16+
`concurrent.futures <https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor>`_
17+
module.
18+
19+
Example::
20+
21+
from concurrent.futures import ProcessPoolExecutor
22+
23+
from lxns.namespaces import UserNamespace
24+
25+
26+
def test() -> int:
27+
UserNamespace.unshare()
28+
return UserNamespace.get_current_ns_id()
29+
30+
31+
def main() -> None:
32+
print("My user NS id:", UserNamespace.get_current_ns_id())
33+
34+
with ProcessPoolExecutor() as executor:
35+
print("Subprocess user NS id:", executor.submit(test).result(1))
36+
37+
print("My user NS id after:", UserNamespace.get_current_ns_id())
38+
39+
40+
if __name__ == "__main__":
41+
main()
42+
43+
44+
Executors can also be used with non-blocking asyncio::
45+
46+
from asyncio import get_running_loop
47+
from asyncio import run as asyncio_run
48+
from concurrent.futures import ProcessPoolExecutor
49+
50+
from lxns.namespaces import UserNamespace
51+
52+
53+
def test() -> int:
54+
UserNamespace.unshare()
55+
return UserNamespace.get_current_ns_id()
56+
57+
58+
async def main() -> None:
59+
print("My user NS id:", UserNamespace.get_current_ns_id())
60+
61+
loop = get_running_loop()
62+
63+
with ProcessPoolExecutor() as executor:
64+
fut = loop.run_in_executor(executor, test)
65+
print("Not blocked!")
66+
print("Subprocess user NS id:", await fut)
67+
68+
print("My user NS id after:", UserNamespace.get_current_ns_id())
69+
70+
71+
if __name__ == "__main__":
72+
asyncio_run(main())
73+
74+
The downside is that `only functions that can be pickled <https://python.readthedocs.io/en/stable/library/pickle.html#what-can-be-pickled-and-unpickled>`_
75+
are supported.

examples/process_executor.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-License-Identifier: MIT
2+
# SPDX-FileCopyrightText: 2024 igo95862
3+
from concurrent.futures import ProcessPoolExecutor
4+
5+
from lxns.namespaces import UserNamespace
6+
7+
8+
def test() -> int:
9+
UserNamespace.unshare()
10+
return UserNamespace.get_current_ns_id()
11+
12+
13+
def main() -> None:
14+
print("My user NS id:", UserNamespace.get_current_ns_id())
15+
16+
with ProcessPoolExecutor() as executor:
17+
print("Subprocess user NS id:", executor.submit(test).result(1))
18+
19+
print("My user NS id after:", UserNamespace.get_current_ns_id())
20+
21+
22+
if __name__ == "__main__":
23+
main()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-License-Identifier: MIT
2+
# SPDX-FileCopyrightText: 2024 igo95862
3+
from asyncio import get_running_loop
4+
from asyncio import run as asyncio_run
5+
from concurrent.futures import ProcessPoolExecutor
6+
7+
from lxns.namespaces import UserNamespace
8+
9+
10+
def test() -> int:
11+
UserNamespace.unshare()
12+
return UserNamespace.get_current_ns_id()
13+
14+
15+
async def main() -> None:
16+
print("My user NS id:", UserNamespace.get_current_ns_id())
17+
18+
loop = get_running_loop()
19+
20+
with ProcessPoolExecutor() as executor:
21+
fut = loop.run_in_executor(executor, test)
22+
print("Not blocked!")
23+
print("Subprocess user NS id:", await fut)
24+
25+
print("My user NS id after:", UserNamespace.get_current_ns_id())
26+
27+
28+
if __name__ == "__main__":
29+
asyncio_run(main())

tools/run_linters.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
PROJECT_ROOT_PATH / "tools",
1313
PROJECT_ROOT_PATH / "test",
1414
PROJECT_ROOT_PATH / "docs/conf.py",
15+
PROJECT_ROOT_PATH / "examples",
1516
]
1617

1718

0 commit comments

Comments
 (0)