Skip to content

Commit 5b3e4a4

Browse files
jefferyto1715173329
authored andcommitted
python-userpath: Add new package
The patches have been submitted upstream in ofek/userpath#52 and ofek/userpath#53. From the README: This is a tool for modifying a user's PATH. Signed-off-by: Jeffery To <[email protected]>
1 parent b354a7e commit 5b3e4a4

5 files changed

Lines changed: 279 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#
2+
# Copyright (C) 2023 Jeffery To
3+
#
4+
# This is free software, licensed under the GNU General Public License v2.
5+
# See /LICENSE for more information.
6+
#
7+
8+
include $(TOPDIR)/rules.mk
9+
10+
PKG_NAME:=python-userpath
11+
PKG_VERSION:=1.9.1
12+
PKG_RELEASE:=1
13+
14+
PYPI_NAME:=userpath
15+
PKG_HASH:=ce8176728d98c914b6401781bf3b23fccd968d1647539c8788c7010375e02796
16+
17+
PKG_LICENSE:=MIT
18+
PKG_LICENSE_FILES:=LICENSE.txt
19+
PKG_MAINTAINER:=Jeffery To <[email protected]>
20+
21+
PKG_BUILD_DEPENDS:=python-hatchling/host
22+
23+
include ../pypi.mk
24+
include $(INCLUDE_DIR)/package.mk
25+
include ../python3-package.mk
26+
27+
define Package/python3-userpath
28+
SECTION:=lang
29+
CATEGORY:=Languages
30+
SUBMENU:=Python
31+
TITLE:=Cross-platform tool for modifying a user's PATH
32+
URL:=https://github.com/ofek/userpath
33+
DEPENDS:=+python3-light +python3-click +python3-psutil
34+
endef
35+
36+
define Package/python3-userpath/description
37+
This is a tool for modifying a user's PATH.
38+
endef
39+
40+
$(eval $(call Py3Package,python3-userpath))
41+
$(eval $(call BuildPackage,python3-userpath))
42+
$(eval $(call BuildPackage,python3-userpath-src))
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
From 9175a0a97c7bc2eeb995e53d50a07be6a7e834f0 Mon Sep 17 00:00:00 2001
2+
From: Jeffery To <[email protected]>
3+
Date: Thu, 9 Nov 2023 14:20:58 +0800
4+
Subject: [PATCH] Handle OSErrors when running show path commands
5+
6+
Bash may not always be installed, for example on OpenWrt, and attempting
7+
to call the show path commands for Bash will cause a FileNotFoundError
8+
to be raised.
9+
10+
This wraps the subprocess call with a try statement and returns the
11+
empty string in the case of an OSError.
12+
---
13+
userpath/utils.py | 7 +++++--
14+
1 file changed, 5 insertions(+), 2 deletions(-)
15+
16+
--- a/userpath/utils.py
17+
+++ b/userpath/utils.py
18+
@@ -30,8 +30,11 @@ def ensure_parent_dir_exists(path):
19+
20+
21+
def get_flat_output(command, sep=os.pathsep, **kwargs):
22+
- process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
23+
- output = process.communicate()[0].decode(locale.getpreferredencoding(False)).strip()
24+
+ try:
25+
+ process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
26+
+ output = process.communicate()[0].decode(locale.getpreferredencoding(False)).strip()
27+
+ except OSError:
28+
+ return ''
29+
30+
# We do this because the output may contain new lines.
31+
lines = [line.strip() for line in output.splitlines()]
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
From dffcc1c5823bcce10b420467db41e42ec41f4702 Mon Sep 17 00:00:00 2001
2+
From: Jeffery To <[email protected]>
3+
Date: Thu, 9 Nov 2023 17:48:50 +0800
4+
Subject: [PATCH 1/2] Use Sh as base class for Bash and Zsh
5+
6+
---
7+
userpath/shells.py | 41 ++++++++++++++++++++++++++---------------
8+
1 file changed, 26 insertions(+), 15 deletions(-)
9+
10+
--- a/userpath/shells.py
11+
+++ b/userpath/shells.py
12+
@@ -12,24 +12,36 @@ class Shell(object):
13+
14+
15+
class Sh(Shell):
16+
- def config(self, location, front=True):
17+
+ name = 'sh'
18+
+
19+
+ def _config_contents(self, location, front=True):
20+
head, tail = (location, '$PATH') if front else ('$PATH', location)
21+
new_path = '{}{}{}'.format(head, pathsep, tail)
22+
+ return 'export PATH="{}"'.format(new_path)
23+
+
24+
+ def config(self, location, front=True):
25+
+ contents = self._config_contents(location, front=front)
26+
+ return {path.join(self.home, '.profile'): contents}
27+
28+
- return {path.join(self.home, '.profile'): 'PATH="{}"'.format(new_path)}
29+
+ @classmethod
30+
+ def _interactive_show_path_command(cls):
31+
+ return [cls.name, '-i', '-c', 'echo $PATH']
32+
+
33+
+ @classmethod
34+
+ def _interactive_login_show_path_command(cls):
35+
+ return [cls.name, '-i', '-l', '-c', 'echo $PATH']
36+
37+
@classmethod
38+
def show_path_commands(cls):
39+
# TODO: Find out what file influences non-login shells. The issue may simply be our Docker setup.
40+
- return [['sh', '-i', '-l', '-c', 'echo $PATH']]
41+
+ return [cls._interactive_login_show_path_command()]
42+
43+
44+
-class Bash(Shell):
45+
- def config(self, location, front=True):
46+
- head, tail = (location, '$PATH') if front else ('$PATH', location)
47+
- new_path = '{}{}{}'.format(head, pathsep, tail)
48+
- contents = 'export PATH="{}"'.format(new_path)
49+
+class Bash(Sh):
50+
+ name = 'bash'
51+
52+
+ def config(self, location, front=True):
53+
+ contents = self._config_contents(location, front=front)
54+
configs = {path.join(self.home, '.bashrc'): contents}
55+
56+
# https://github.com/ofek/userpath/issues/3#issuecomment-492491977
57+
@@ -50,7 +62,7 @@ class Bash(Shell):
58+
59+
@classmethod
60+
def show_path_commands(cls):
61+
- return [['bash', '-i', '-c', 'echo $PATH'], ['bash', '-i', '-l', '-c', 'echo $PATH']]
62+
+ return [cls._interactive_show_path_command(), cls._interactive_login_show_path_command()]
63+
64+
65+
class Fish(Shell):
66+
@@ -88,18 +100,17 @@ class Xonsh(Shell):
67+
return [['xonsh', '-i', '-c', command], ['xonsh', '-i', '--login', '-c', command]]
68+
69+
70+
-class Zsh(Shell):
71+
- def config(self, location, front=True):
72+
- head, tail = (location, '$PATH') if front else ('$PATH', location)
73+
- new_path = '{}{}{}'.format(head, pathsep, tail)
74+
- contents = 'export PATH="{}"'.format(new_path)
75+
+class Zsh(Sh):
76+
+ name = 'zsh'
77+
78+
+ def config(self, location, front=True):
79+
+ contents = self._config_contents(location, front=front)
80+
zdotdir = environ.get('ZDOTDIR', self.home)
81+
return {path.join(zdotdir, '.zshrc'): contents, path.join(zdotdir, '.zprofile'): contents}
82+
83+
@classmethod
84+
def show_path_commands(cls):
85+
- return [['zsh', '-i', '-c', 'echo $PATH'], ['zsh', '-i', '-l', '-c', 'echo $PATH']]
86+
+ return [cls._interactive_show_path_command(), cls._interactive_login_show_path_command()]
87+
88+
89+
SHELLS = {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
From 7823b9b39c486aedf830783329abdc3bd9664ba4 Mon Sep 17 00:00:00 2001
2+
From: Jeffery To <[email protected]>
3+
Date: Thu, 9 Nov 2023 17:51:21 +0800
4+
Subject: [PATCH 2/2] Add support for ash (Almquist shell)
5+
6+
---
7+
tests/docker/debian | 2 +-
8+
tests/test_ash.py | 65 +++++++++++++++++++++++++++++++++++++++++++++
9+
userpath/shells.py | 5 ++++
10+
3 files changed, 71 insertions(+), 1 deletion(-)
11+
create mode 100644 tests/test_ash.py
12+
13+
--- a/tests/docker/debian
14+
+++ b/tests/docker/debian
15+
@@ -2,7 +2,7 @@ ARG PYTHON_VERSION
16+
FROM python:${PYTHON_VERSION}
17+
18+
RUN apt-get update \
19+
- && apt-get --no-install-recommends -y install fish zsh
20+
+ && apt-get --no-install-recommends -y install ash fish zsh
21+
22+
COPY requirements.txt /
23+
RUN pip install -r requirements.txt
24+
--- /dev/null
25+
+++ b/tests/test_ash.py
26+
@@ -0,0 +1,65 @@
27+
+import pytest
28+
+import userpath
29+
+
30+
+from .utils import SKIP_WINDOWS_CI, get_random_path
31+
+
32+
+SHELL_NAME = 'ash'
33+
+
34+
+pytestmark = [SKIP_WINDOWS_CI, pytest.mark.ash]
35+
+
36+
+
37+
+@pytest.mark.usefixtures('shell_test')
38+
+class TestDebian(object):
39+
+ DOCKERFILE = 'debian'
40+
+
41+
+ def test_prepend(self, request, shell_test):
42+
+ if shell_test is None:
43+
+ location = get_random_path()
44+
+ assert not userpath.in_current_path(location)
45+
+ assert userpath.prepend(location, check=True)
46+
+ assert userpath.in_new_path(location)
47+
+ assert userpath.need_shell_restart(location)
48+
+ else:
49+
+ process = shell_test(request.node.name)
50+
+ stdout, stderr = process.communicate()
51+
+
52+
+ assert process.returncode == 0, (stdout + stderr).decode('utf-8')
53+
+
54+
+ def test_prepend_multiple(self, request, shell_test):
55+
+ if shell_test is None:
56+
+ locations = [get_random_path(), get_random_path()]
57+
+ assert not userpath.in_current_path(locations)
58+
+ assert userpath.prepend(locations, check=True)
59+
+ assert userpath.in_new_path(locations)
60+
+ assert userpath.need_shell_restart(locations)
61+
+ else:
62+
+ process = shell_test(request.node.name)
63+
+ stdout, stderr = process.communicate()
64+
+
65+
+ assert process.returncode == 0, (stdout + stderr).decode('utf-8')
66+
+
67+
+ def test_append(self, request, shell_test):
68+
+ if shell_test is None:
69+
+ location = get_random_path()
70+
+ assert not userpath.in_current_path(location)
71+
+ assert userpath.append(location, check=True)
72+
+ assert userpath.in_new_path(location)
73+
+ assert userpath.need_shell_restart(location)
74+
+ else:
75+
+ process = shell_test(request.node.name)
76+
+ stdout, stderr = process.communicate()
77+
+
78+
+ assert process.returncode == 0, (stdout + stderr).decode('utf-8')
79+
+
80+
+ def test_append_multiple(self, request, shell_test):
81+
+ if shell_test is None:
82+
+ locations = [get_random_path(), get_random_path()]
83+
+ assert not userpath.in_current_path(locations)
84+
+ assert userpath.append(locations, check=True)
85+
+ assert userpath.in_new_path(locations)
86+
+ assert userpath.need_shell_restart(locations)
87+
+ else:
88+
+ process = shell_test(request.node.name)
89+
+ stdout, stderr = process.communicate()
90+
+
91+
+ assert process.returncode == 0, (stdout + stderr).decode('utf-8')
92+
--- a/userpath/shells.py
93+
+++ b/userpath/shells.py
94+
@@ -37,6 +37,10 @@ class Sh(Shell):
95+
return [cls._interactive_login_show_path_command()]
96+
97+
98+
+class Ash(Sh):
99+
+ name = 'ash'
100+
+
101+
+
102+
class Bash(Sh):
103+
name = 'bash'
104+
105+
@@ -114,6 +118,7 @@ class Zsh(Sh):
106+
107+
108+
SHELLS = {
109+
+ 'ash': Ash,
110+
'bash': Bash,
111+
'fish': Fish,
112+
'sh': Sh,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/sh
2+
3+
[ "$1" = python3-userpath ] || exit 0
4+
5+
userpath --version | grep -Fx "userpath, version $PKG_VERSION"

0 commit comments

Comments
 (0)