AllIllusion commited on
Commit
6e749b0
·
1 Parent(s): 1524f5b

Add SentimentAnalysis for both Gradio and Docker streamlit

Browse files
Files changed (5) hide show
  1. Dockerfile +46 -0
  2. app.py +62 -0
  3. os.py__Python3.9 +1119 -0
  4. requirements.txt +2 -0
  5. streamlit_app.py +64 -0
Dockerfile ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y \
6
+ build-essential \
7
+ curl \
8
+ software-properties-common \
9
+ git \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ COPY requirements.txt ./
13
+ COPY src/ ./src/
14
+
15
+ # RUN mkdir -p /usr && mkdir -p /usr/src && mkdir -p /usr/src/app && mkdir -p /usr/src/app/flagged \
16
+ # mkdir -p ./src/app && mkdir -p ./src/app/flagged \
17
+
18
+ RUN mkdir -p /app/flagged && chmod 777 /app/flagged
19
+ RUN mkdir -p /.streamlit && chmod 777 /.streamlit
20
+
21
+ #RUN mkdir -p ./.gradio && chmod 777 ./.gradio
22
+ #RUN mkdir -p ./.gradio/flagged && chmod 777 ./.gradio/flagged
23
+ #RUN mkdir -p ./.streamlit && chmod 777 ./.streamlit
24
+ #RUN mkdir -p /app/.gradio && chmod 777 /app/.gradio
25
+ #RUN mkdir -p /app/.gradio/flagged && chmod 777 /app/.gradio/flagged
26
+ #RUN mkdir -p /.gradio && chmod 777 /.gradio
27
+ #RUN mkdir -p /.gradio/flagged && chmod 777 /.gradio/flagged
28
+ #RUN mkdir -p /app/src/.gradio && chmod 777 /app/src/.gradio
29
+ #RUN mkdir -p /app/src/.gradio/flagged && chmod 777 /app/src/.gradio/flagged
30
+ #RUN mkdir -p /app/src/.streamlit && chmod 777 /app/src/.streamlit
31
+ #RUN mkdir -p /app/src/app/flagged && chmod 777 /app/src/app/flagged
32
+ #RUN mkdir -p /app/src/app/.gradio && chmod 777 /app/src/app/.gradio
33
+ #RUN mkdir -p /app/src/app/.gradio/flagged && chmod 777 /app/src/app/.gradio/flagged
34
+ #RUN chmod 777 /app/src/app && chmod 777 /app/src/*
35
+
36
+ RUN pip3 install -r requirements.txt
37
+
38
+ # COPY ./os.py /usr/local/lib/python3.9/os.py
39
+ COPY ./frpc_linux_amd64_v0.3 /.cache/huggingface/gradio/frpc/frpc_linux_amd64_v0.3
40
+
41
+
42
+ EXPOSE 8501
43
+
44
+ HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
45
+
46
+ ENTRYPOINT ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
app.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #########################################################################
2
+ # Copyright (C) #
3
+ # 2025-June Sen Li (Sen.Li.Sprout@gmail.com) #
4
+ # Permission given to modify the code only for Non-Profit Research #
5
+ # as long as you keep this declaration at the top #
6
+ #########################################################################
7
+
8
+ import gradio as gr
9
+ import textblob
10
+ import os
11
+
12
+ def func_SentimentAnalysis(str_tgtText_2_Analyze: str) -> dict:
13
+ """ Analyze the sentiment of the given text.
14
+ Args:
15
+ str_tgtText_2_Analyze (str): The text to analyze
16
+
17
+ Returns:
18
+ dict_SentimentAnalysis: { polarity [-1, 1],
19
+ subjectivity objective [0, 1] subjective
20
+ assessment ["positive", "neutral", "negative"]
21
+ }
22
+ """
23
+ textBlob_tgtText = textblob.TextBlob(str_tgtText_2_Analyze)
24
+ sentiment_tgtText = textBlob_tgtText.sentiment
25
+ dict_SentimentAnalysis = {
26
+ "polarity": round(sentiment_tgtText.polarity, 2),
27
+ "subjectivity": round(sentiment_tgtText.subjectivity, 2),
28
+ "assessment": "positive" if sentiment_tgtText.polarity > 0 \
29
+ else "negative" if sentiment_tgtText.polarity < 0 else "neutral"
30
+ }
31
+ return dict_SentimentAnalysis
32
+
33
+
34
+
35
+
36
+ # Launch the interface and MCP server
37
+ if __name__ == "__main__":
38
+ print(f"os.getcwd() = {os.getcwd()}")
39
+ os.system(f"echo ls -al {os.getcwd()} && ls -al {os.getcwd()}")
40
+ os.system(f"echo ls -al /: && ls -al /")
41
+ os.system(f"echo ls -al /app/: && ls -al /app/")
42
+ os.system(f"echo ls -al /home/: && ls -al /home/")
43
+
44
+ # Create the Gradio interface
45
+ grInterface_SentimentAnalysis = gr.Interface(
46
+ fn = func_SentimentAnalysis,
47
+ inputs = gr.Textbox(placeholder="Enter text to analyze..."),
48
+ outputs = gr.JSON(),
49
+ title = "Text Sentiment Analysis",
50
+ description = "Analyze the sentiment of text using TextBlob"
51
+ )
52
+
53
+
54
+ # MCP Schema: Visit http://localhost:7860/gradio_api/mcp/schema
55
+ # MCP Server:
56
+ # Setting mcp_server=True enables the MCP server, enable using the environment variable:
57
+ # export GRADIO_MCP_SERVER=True # http://localhost:7860/gradio_api/mcp/sse
58
+
59
+ grInterface_SentimentAnalysis.launch(mcp_server=True, share=True)
60
+
61
+ # os.system(f"echo ls -al /app/flagged/: && ls -al /app/flagged/")
62
+ # os.system(f"echo ls -al /.gradio/flagged/: && ls -al /.gradio/flagged/")
os.py__Python3.9 ADDED
@@ -0,0 +1,1119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ r"""OS routines for NT or Posix depending on what system we're on.
2
+
3
+ This exports:
4
+ - all functions from posix or nt, e.g. unlink, stat, etc.
5
+ - os.path is either posixpath or ntpath
6
+ - os.name is either 'posix' or 'nt'
7
+ - os.curdir is a string representing the current directory (always '.')
8
+ - os.pardir is a string representing the parent directory (always '..')
9
+ - os.sep is the (or a most common) pathname separator ('/' or '\\')
10
+ - os.extsep is the extension separator (always '.')
11
+ - os.altsep is the alternate pathname separator (None or '/')
12
+ - os.pathsep is the component separator used in $PATH etc
13
+ - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n')
14
+ - os.defpath is the default search path for executables
15
+ - os.devnull is the file path of the null device ('/dev/null', etc.)
16
+
17
+ Programs that import and use 'os' stand a better chance of being
18
+ portable between different platforms. Of course, they must then
19
+ only use functions that are defined by all platforms (e.g., unlink
20
+ and opendir), and leave all pathname manipulation to os.path
21
+ (e.g., split and join).
22
+ """
23
+
24
+ #'
25
+ import abc
26
+ import sys
27
+ import stat as st
28
+
29
+ from _collections_abc import _check_methods
30
+
31
+ GenericAlias = type(list[int])
32
+
33
+ _names = sys.builtin_module_names
34
+
35
+ # Note: more names are added to __all__ later.
36
+ __all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep",
37
+ "defpath", "name", "path", "devnull", "SEEK_SET", "SEEK_CUR",
38
+ "SEEK_END", "fsencode", "fsdecode", "get_exec_path", "fdopen",
39
+ "popen", "extsep"]
40
+
41
+ def _exists(name):
42
+ return name in globals()
43
+
44
+ def _get_exports_list(module):
45
+ try:
46
+ return list(module.__all__)
47
+ except AttributeError:
48
+ return [n for n in dir(module) if n[0] != '_']
49
+
50
+ # Any new dependencies of the os module and/or changes in path separator
51
+ # requires updating importlib as well.
52
+ if 'posix' in _names:
53
+ name = 'posix'
54
+ linesep = '\n'
55
+ from posix import *
56
+ try:
57
+ from posix import _exit
58
+ __all__.append('_exit')
59
+ except ImportError:
60
+ pass
61
+ import posixpath as path
62
+
63
+ try:
64
+ from posix import _have_functions
65
+ except ImportError:
66
+ pass
67
+
68
+ import posix
69
+ __all__.extend(_get_exports_list(posix))
70
+ del posix
71
+
72
+ elif 'nt' in _names:
73
+ name = 'nt'
74
+ linesep = '\r\n'
75
+ from nt import *
76
+ try:
77
+ from nt import _exit
78
+ __all__.append('_exit')
79
+ except ImportError:
80
+ pass
81
+ import ntpath as path
82
+
83
+ import nt
84
+ __all__.extend(_get_exports_list(nt))
85
+ del nt
86
+
87
+ try:
88
+ from nt import _have_functions
89
+ except ImportError:
90
+ pass
91
+
92
+ else:
93
+ raise ImportError('no os specific module found')
94
+
95
+ sys.modules['os.path'] = path
96
+ from os.path import (curdir, pardir, sep, pathsep, defpath, extsep, altsep,
97
+ devnull)
98
+
99
+ del _names
100
+
101
+
102
+ if _exists("_have_functions"):
103
+ _globals = globals()
104
+ def _add(str, fn):
105
+ if (fn in _globals) and (str in _have_functions):
106
+ _set.add(_globals[fn])
107
+
108
+ _set = set()
109
+ _add("HAVE_FACCESSAT", "access")
110
+ _add("HAVE_FCHMODAT", "chmod")
111
+ _add("HAVE_FCHOWNAT", "chown")
112
+ _add("HAVE_FSTATAT", "stat")
113
+ _add("HAVE_FUTIMESAT", "utime")
114
+ _add("HAVE_LINKAT", "link")
115
+ _add("HAVE_MKDIRAT", "mkdir")
116
+ _add("HAVE_MKFIFOAT", "mkfifo")
117
+ _add("HAVE_MKNODAT", "mknod")
118
+ _add("HAVE_OPENAT", "open")
119
+ _add("HAVE_READLINKAT", "readlink")
120
+ _add("HAVE_RENAMEAT", "rename")
121
+ _add("HAVE_SYMLINKAT", "symlink")
122
+ _add("HAVE_UNLINKAT", "unlink")
123
+ _add("HAVE_UNLINKAT", "rmdir")
124
+ _add("HAVE_UTIMENSAT", "utime")
125
+ supports_dir_fd = _set
126
+
127
+ _set = set()
128
+ _add("HAVE_FACCESSAT", "access")
129
+ supports_effective_ids = _set
130
+
131
+ _set = set()
132
+ _add("HAVE_FCHDIR", "chdir")
133
+ _add("HAVE_FCHMOD", "chmod")
134
+ _add("HAVE_FCHOWN", "chown")
135
+ _add("HAVE_FDOPENDIR", "listdir")
136
+ _add("HAVE_FDOPENDIR", "scandir")
137
+ _add("HAVE_FEXECVE", "execve")
138
+ _set.add(stat) # fstat always works
139
+ _add("HAVE_FTRUNCATE", "truncate")
140
+ _add("HAVE_FUTIMENS", "utime")
141
+ _add("HAVE_FUTIMES", "utime")
142
+ _add("HAVE_FPATHCONF", "pathconf")
143
+ if _exists("statvfs") and _exists("fstatvfs"): # mac os x10.3
144
+ _add("HAVE_FSTATVFS", "statvfs")
145
+ supports_fd = _set
146
+
147
+ _set = set()
148
+ _add("HAVE_FACCESSAT", "access")
149
+ # Some platforms don't support lchmod(). Often the function exists
150
+ # anyway, as a stub that always returns ENOSUP or perhaps EOPNOTSUPP.
151
+ # (No, I don't know why that's a good design.) ./configure will detect
152
+ # this and reject it--so HAVE_LCHMOD still won't be defined on such
153
+ # platforms. This is Very Helpful.
154
+ #
155
+ # However, sometimes platforms without a working lchmod() *do* have
156
+ # fchmodat(). (Examples: Linux kernel 3.2 with glibc 2.15,
157
+ # OpenIndiana 3.x.) And fchmodat() has a flag that theoretically makes
158
+ # it behave like lchmod(). So in theory it would be a suitable
159
+ # replacement for lchmod(). But when lchmod() doesn't work, fchmodat()'s
160
+ # flag doesn't work *either*. Sadly ./configure isn't sophisticated
161
+ # enough to detect this condition--it only determines whether or not
162
+ # fchmodat() minimally works.
163
+ #
164
+ # Therefore we simply ignore fchmodat() when deciding whether or not
165
+ # os.chmod supports follow_symlinks. Just checking lchmod() is
166
+ # sufficient. After all--if you have a working fchmodat(), your
167
+ # lchmod() almost certainly works too.
168
+ #
169
+ # _add("HAVE_FCHMODAT", "chmod")
170
+ _add("HAVE_FCHOWNAT", "chown")
171
+ _add("HAVE_FSTATAT", "stat")
172
+ _add("HAVE_LCHFLAGS", "chflags")
173
+ _add("HAVE_LCHMOD", "chmod")
174
+ if _exists("lchown"): # mac os x10.3
175
+ _add("HAVE_LCHOWN", "chown")
176
+ _add("HAVE_LINKAT", "link")
177
+ _add("HAVE_LUTIMES", "utime")
178
+ _add("HAVE_LSTAT", "stat")
179
+ _add("HAVE_FSTATAT", "stat")
180
+ _add("HAVE_UTIMENSAT", "utime")
181
+ _add("MS_WINDOWS", "stat")
182
+ supports_follow_symlinks = _set
183
+
184
+ del _set
185
+ del _have_functions
186
+ del _globals
187
+ del _add
188
+
189
+
190
+ # Python uses fixed values for the SEEK_ constants; they are mapped
191
+ # to native constants if necessary in posixmodule.c
192
+ # Other possible SEEK values are directly imported from posixmodule.c
193
+ SEEK_SET = 0
194
+ SEEK_CUR = 1
195
+ SEEK_END = 2
196
+
197
+ # Super directory utilities.
198
+ # (Inspired by Eric Raymond; the doc strings are mostly his)
199
+
200
+ def makedirs(name, mode=0o777, exist_ok=False):
201
+ """makedirs(name [, mode=0o777][, exist_ok=False])
202
+
203
+ Super-mkdir; create a leaf directory and all intermediate ones. Works like
204
+ mkdir, except that any intermediate path segment (not just the rightmost)
205
+ will be created if it does not exist. If the target directory already
206
+ exists, raise an OSError if exist_ok is False. Otherwise no exception is
207
+ raised. This is recursive.
208
+
209
+ """
210
+ head, tail = path.split(name)
211
+
212
+ print(f"path.abspath(name) = {path.abspath(name)}, \n name = {name}, mode = {mode}, exist_ok={exist_ok}", flush=True)
213
+ if not tail:
214
+ head, tail = path.split(head)
215
+ if head and tail and not path.exists(head):
216
+ try:
217
+ makedirs(head, exist_ok=exist_ok)
218
+ except FileExistsError:
219
+ # Defeats race condition when another thread created the path
220
+ pass
221
+ cdir = curdir
222
+ if isinstance(tail, bytes):
223
+ cdir = bytes(curdir, 'ASCII')
224
+ if tail == cdir: # xxx/newdir/. exists if xxx/newdir exists
225
+ return
226
+ try:
227
+ print(f"name = {name}, mode = {mode}\n \t path.abspath(name) = {path.abspath(name)}")
228
+ mkdir(name, mode)
229
+ except OSError:
230
+ # Cannot rely on checking for EEXIST, since the operating system
231
+ # could give priority to other errors like EACCES or EROFS
232
+ if not exist_ok or not path.isdir(name):
233
+ raise
234
+
235
+ def removedirs(name):
236
+ """removedirs(name)
237
+
238
+ Super-rmdir; remove a leaf directory and all empty intermediate
239
+ ones. Works like rmdir except that, if the leaf directory is
240
+ successfully removed, directories corresponding to rightmost path
241
+ segments will be pruned away until either the whole path is
242
+ consumed or an error occurs. Errors during this latter phase are
243
+ ignored -- they generally mean that a directory was not empty.
244
+
245
+ """
246
+ rmdir(name)
247
+ head, tail = path.split(name)
248
+ if not tail:
249
+ head, tail = path.split(head)
250
+ while head and tail:
251
+ try:
252
+ rmdir(head)
253
+ except OSError:
254
+ break
255
+ head, tail = path.split(head)
256
+
257
+ def renames(old, new):
258
+ """renames(old, new)
259
+
260
+ Super-rename; create directories as necessary and delete any left
261
+ empty. Works like rename, except creation of any intermediate
262
+ directories needed to make the new pathname good is attempted
263
+ first. After the rename, directories corresponding to rightmost
264
+ path segments of the old name will be pruned until either the
265
+ whole path is consumed or a nonempty directory is found.
266
+
267
+ Note: this function can fail with the new directory structure made
268
+ if you lack permissions needed to unlink the leaf directory or
269
+ file.
270
+
271
+ """
272
+ head, tail = path.split(new)
273
+ if head and tail and not path.exists(head):
274
+ makedirs(head)
275
+ rename(old, new)
276
+ head, tail = path.split(old)
277
+ if head and tail:
278
+ try:
279
+ removedirs(head)
280
+ except OSError:
281
+ pass
282
+
283
+ __all__.extend(["makedirs", "removedirs", "renames"])
284
+
285
+ def walk(top, topdown=True, onerror=None, followlinks=False):
286
+ """Directory tree generator.
287
+
288
+ For each directory in the directory tree rooted at top (including top
289
+ itself, but excluding '.' and '..'), yields a 3-tuple
290
+
291
+ dirpath, dirnames, filenames
292
+
293
+ dirpath is a string, the path to the directory. dirnames is a list of
294
+ the names of the subdirectories in dirpath (excluding '.' and '..').
295
+ filenames is a list of the names of the non-directory files in dirpath.
296
+ Note that the names in the lists are just names, with no path components.
297
+ To get a full path (which begins with top) to a file or directory in
298
+ dirpath, do os.path.join(dirpath, name).
299
+
300
+ If optional arg 'topdown' is true or not specified, the triple for a
301
+ directory is generated before the triples for any of its subdirectories
302
+ (directories are generated top down). If topdown is false, the triple
303
+ for a directory is generated after the triples for all of its
304
+ subdirectories (directories are generated bottom up).
305
+
306
+ When topdown is true, the caller can modify the dirnames list in-place
307
+ (e.g., via del or slice assignment), and walk will only recurse into the
308
+ subdirectories whose names remain in dirnames; this can be used to prune the
309
+ search, or to impose a specific order of visiting. Modifying dirnames when
310
+ topdown is false has no effect on the behavior of os.walk(), since the
311
+ directories in dirnames have already been generated by the time dirnames
312
+ itself is generated. No matter the value of topdown, the list of
313
+ subdirectories is retrieved before the tuples for the directory and its
314
+ subdirectories are generated.
315
+
316
+ By default errors from the os.scandir() call are ignored. If
317
+ optional arg 'onerror' is specified, it should be a function; it
318
+ will be called with one argument, an OSError instance. It can
319
+ report the error to continue with the walk, or raise the exception
320
+ to abort the walk. Note that the filename is available as the
321
+ filename attribute of the exception object.
322
+
323
+ By default, os.walk does not follow symbolic links to subdirectories on
324
+ systems that support them. In order to get this functionality, set the
325
+ optional argument 'followlinks' to true.
326
+
327
+ Caution: if you pass a relative pathname for top, don't change the
328
+ current working directory between resumptions of walk. walk never
329
+ changes the current directory, and assumes that the client doesn't
330
+ either.
331
+
332
+ Example:
333
+
334
+ import os
335
+ from os.path import join, getsize
336
+ for root, dirs, files in os.walk('python/Lib/email'):
337
+ print(root, "consumes", end="")
338
+ print(sum(getsize(join(root, name)) for name in files), end="")
339
+ print("bytes in", len(files), "non-directory files")
340
+ if 'CVS' in dirs:
341
+ dirs.remove('CVS') # don't visit CVS directories
342
+
343
+ """
344
+ sys.audit("os.walk", top, topdown, onerror, followlinks)
345
+ return _walk(fspath(top), topdown, onerror, followlinks)
346
+
347
+ def _walk(top, topdown, onerror, followlinks):
348
+ dirs = []
349
+ nondirs = []
350
+ walk_dirs = []
351
+
352
+ # We may not have read permission for top, in which case we can't
353
+ # get a list of the files the directory contains. os.walk
354
+ # always suppressed the exception then, rather than blow up for a
355
+ # minor reason when (say) a thousand readable directories are still
356
+ # left to visit. That logic is copied here.
357
+ try:
358
+ # Note that scandir is global in this module due
359
+ # to earlier import-*.
360
+ scandir_it = scandir(top)
361
+ except OSError as error:
362
+ if onerror is not None:
363
+ onerror(error)
364
+ return
365
+
366
+ with scandir_it:
367
+ while True:
368
+ try:
369
+ try:
370
+ entry = next(scandir_it)
371
+ except StopIteration:
372
+ break
373
+ except OSError as error:
374
+ if onerror is not None:
375
+ onerror(error)
376
+ return
377
+
378
+ try:
379
+ is_dir = entry.is_dir()
380
+ except OSError:
381
+ # If is_dir() raises an OSError, consider that the entry is not
382
+ # a directory, same behaviour than os.path.isdir().
383
+ is_dir = False
384
+
385
+ if is_dir:
386
+ dirs.append(entry.name)
387
+ else:
388
+ nondirs.append(entry.name)
389
+
390
+ if not topdown and is_dir:
391
+ # Bottom-up: recurse into sub-directory, but exclude symlinks to
392
+ # directories if followlinks is False
393
+ if followlinks:
394
+ walk_into = True
395
+ else:
396
+ try:
397
+ is_symlink = entry.is_symlink()
398
+ except OSError:
399
+ # If is_symlink() raises an OSError, consider that the
400
+ # entry is not a symbolic link, same behaviour than
401
+ # os.path.islink().
402
+ is_symlink = False
403
+ walk_into = not is_symlink
404
+
405
+ if walk_into:
406
+ walk_dirs.append(entry.path)
407
+
408
+ # Yield before recursion if going top down
409
+ if topdown:
410
+ yield top, dirs, nondirs
411
+
412
+ # Recurse into sub-directories
413
+ islink, join = path.islink, path.join
414
+ for dirname in dirs:
415
+ new_path = join(top, dirname)
416
+ # Issue #23605: os.path.islink() is used instead of caching
417
+ # entry.is_symlink() result during the loop on os.scandir() because
418
+ # the caller can replace the directory entry during the "yield"
419
+ # above.
420
+ if followlinks or not islink(new_path):
421
+ yield from _walk(new_path, topdown, onerror, followlinks)
422
+ else:
423
+ # Recurse into sub-directories
424
+ for new_path in walk_dirs:
425
+ yield from _walk(new_path, topdown, onerror, followlinks)
426
+ # Yield after recursion if going bottom up
427
+ yield top, dirs, nondirs
428
+
429
+ __all__.append("walk")
430
+
431
+ if {open, stat} <= supports_dir_fd and {scandir, stat} <= supports_fd:
432
+
433
+ def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None):
434
+ """Directory tree generator.
435
+
436
+ This behaves exactly like walk(), except that it yields a 4-tuple
437
+
438
+ dirpath, dirnames, filenames, dirfd
439
+
440
+ `dirpath`, `dirnames` and `filenames` are identical to walk() output,
441
+ and `dirfd` is a file descriptor referring to the directory `dirpath`.
442
+
443
+ The advantage of fwalk() over walk() is that it's safe against symlink
444
+ races (when follow_symlinks is False).
445
+
446
+ If dir_fd is not None, it should be a file descriptor open to a directory,
447
+ and top should be relative; top will then be relative to that directory.
448
+ (dir_fd is always supported for fwalk.)
449
+
450
+ Caution:
451
+ Since fwalk() yields file descriptors, those are only valid until the
452
+ next iteration step, so you should dup() them if you want to keep them
453
+ for a longer period.
454
+
455
+ Example:
456
+
457
+ import os
458
+ for root, dirs, files, rootfd in os.fwalk('python/Lib/email'):
459
+ print(root, "consumes", end="")
460
+ print(sum(os.stat(name, dir_fd=rootfd).st_size for name in files),
461
+ end="")
462
+ print("bytes in", len(files), "non-directory files")
463
+ if 'CVS' in dirs:
464
+ dirs.remove('CVS') # don't visit CVS directories
465
+ """
466
+ sys.audit("os.fwalk", top, topdown, onerror, follow_symlinks, dir_fd)
467
+ if not isinstance(top, int) or not hasattr(top, '__index__'):
468
+ top = fspath(top)
469
+ # Note: To guard against symlink races, we use the standard
470
+ # lstat()/open()/fstat() trick.
471
+ if not follow_symlinks:
472
+ orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)
473
+ topfd = open(top, O_RDONLY, dir_fd=dir_fd)
474
+ try:
475
+ if (follow_symlinks or (st.S_ISDIR(orig_st.st_mode) and
476
+ path.samestat(orig_st, stat(topfd)))):
477
+ yield from _fwalk(topfd, top, isinstance(top, bytes),
478
+ topdown, onerror, follow_symlinks)
479
+ finally:
480
+ close(topfd)
481
+
482
+ def _fwalk(topfd, toppath, isbytes, topdown, onerror, follow_symlinks):
483
+ # Note: This uses O(depth of the directory tree) file descriptors: if
484
+ # necessary, it can be adapted to only require O(1) FDs, see issue
485
+ # #13734.
486
+
487
+ scandir_it = scandir(topfd)
488
+ dirs = []
489
+ nondirs = []
490
+ entries = None if topdown or follow_symlinks else []
491
+ for entry in scandir_it:
492
+ name = entry.name
493
+ if isbytes:
494
+ name = fsencode(name)
495
+ try:
496
+ if entry.is_dir():
497
+ dirs.append(name)
498
+ if entries is not None:
499
+ entries.append(entry)
500
+ else:
501
+ nondirs.append(name)
502
+ except OSError:
503
+ try:
504
+ # Add dangling symlinks, ignore disappeared files
505
+ if entry.is_symlink():
506
+ nondirs.append(name)
507
+ except OSError:
508
+ pass
509
+
510
+ if topdown:
511
+ yield toppath, dirs, nondirs, topfd
512
+
513
+ for name in dirs if entries is None else zip(dirs, entries):
514
+ try:
515
+ if not follow_symlinks:
516
+ if topdown:
517
+ orig_st = stat(name, dir_fd=topfd, follow_symlinks=False)
518
+ else:
519
+ assert entries is not None
520
+ name, entry = name
521
+ orig_st = entry.stat(follow_symlinks=False)
522
+ dirfd = open(name, O_RDONLY, dir_fd=topfd)
523
+ except OSError as err:
524
+ if onerror is not None:
525
+ onerror(err)
526
+ continue
527
+ try:
528
+ if follow_symlinks or path.samestat(orig_st, stat(dirfd)):
529
+ dirpath = path.join(toppath, name)
530
+ yield from _fwalk(dirfd, dirpath, isbytes,
531
+ topdown, onerror, follow_symlinks)
532
+ finally:
533
+ close(dirfd)
534
+
535
+ if not topdown:
536
+ yield toppath, dirs, nondirs, topfd
537
+
538
+ __all__.append("fwalk")
539
+
540
+ def execl(file, *args):
541
+ """execl(file, *args)
542
+
543
+ Execute the executable file with argument list args, replacing the
544
+ current process. """
545
+ execv(file, args)
546
+
547
+ def execle(file, *args):
548
+ """execle(file, *args, env)
549
+
550
+ Execute the executable file with argument list args and
551
+ environment env, replacing the current process. """
552
+ env = args[-1]
553
+ execve(file, args[:-1], env)
554
+
555
+ def execlp(file, *args):
556
+ """execlp(file, *args)
557
+
558
+ Execute the executable file (which is searched for along $PATH)
559
+ with argument list args, replacing the current process. """
560
+ execvp(file, args)
561
+
562
+ def execlpe(file, *args):
563
+ """execlpe(file, *args, env)
564
+
565
+ Execute the executable file (which is searched for along $PATH)
566
+ with argument list args and environment env, replacing the current
567
+ process. """
568
+ env = args[-1]
569
+ execvpe(file, args[:-1], env)
570
+
571
+ def execvp(file, args):
572
+ """execvp(file, args)
573
+
574
+ Execute the executable file (which is searched for along $PATH)
575
+ with argument list args, replacing the current process.
576
+ args may be a list or tuple of strings. """
577
+ _execvpe(file, args)
578
+
579
+ def execvpe(file, args, env):
580
+ """execvpe(file, args, env)
581
+
582
+ Execute the executable file (which is searched for along $PATH)
583
+ with argument list args and environment env, replacing the
584
+ current process.
585
+ args may be a list or tuple of strings. """
586
+ _execvpe(file, args, env)
587
+
588
+ __all__.extend(["execl","execle","execlp","execlpe","execvp","execvpe"])
589
+
590
+ def _execvpe(file, args, env=None):
591
+ if env is not None:
592
+ exec_func = execve
593
+ argrest = (args, env)
594
+ else:
595
+ exec_func = execv
596
+ argrest = (args,)
597
+ env = environ
598
+
599
+ if path.dirname(file):
600
+ exec_func(file, *argrest)
601
+ return
602
+ saved_exc = None
603
+ path_list = get_exec_path(env)
604
+ if name != 'nt':
605
+ file = fsencode(file)
606
+ path_list = map(fsencode, path_list)
607
+ for dir in path_list:
608
+ fullname = path.join(dir, file)
609
+ try:
610
+ exec_func(fullname, *argrest)
611
+ except (FileNotFoundError, NotADirectoryError) as e:
612
+ last_exc = e
613
+ except OSError as e:
614
+ last_exc = e
615
+ if saved_exc is None:
616
+ saved_exc = e
617
+ if saved_exc is not None:
618
+ raise saved_exc
619
+ raise last_exc
620
+
621
+
622
+ def get_exec_path(env=None):
623
+ """Returns the sequence of directories that will be searched for the
624
+ named executable (similar to a shell) when launching a process.
625
+
626
+ *env* must be an environment variable dict or None. If *env* is None,
627
+ os.environ will be used.
628
+ """
629
+ # Use a local import instead of a global import to limit the number of
630
+ # modules loaded at startup: the os module is always loaded at startup by
631
+ # Python. It may also avoid a bootstrap issue.
632
+ import warnings
633
+
634
+ if env is None:
635
+ env = environ
636
+
637
+ # {b'PATH': ...}.get('PATH') and {'PATH': ...}.get(b'PATH') emit a
638
+ # BytesWarning when using python -b or python -bb: ignore the warning
639
+ with warnings.catch_warnings():
640
+ warnings.simplefilter("ignore", BytesWarning)
641
+
642
+ try:
643
+ path_list = env.get('PATH')
644
+ except TypeError:
645
+ path_list = None
646
+
647
+ if supports_bytes_environ:
648
+ try:
649
+ path_listb = env[b'PATH']
650
+ except (KeyError, TypeError):
651
+ pass
652
+ else:
653
+ if path_list is not None:
654
+ raise ValueError(
655
+ "env cannot contain 'PATH' and b'PATH' keys")
656
+ path_list = path_listb
657
+
658
+ if path_list is not None and isinstance(path_list, bytes):
659
+ path_list = fsdecode(path_list)
660
+
661
+ if path_list is None:
662
+ path_list = defpath
663
+ return path_list.split(pathsep)
664
+
665
+
666
+ # Change environ to automatically call putenv() and unsetenv()
667
+ from _collections_abc import MutableMapping, Mapping
668
+
669
+ class _Environ(MutableMapping):
670
+ def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue):
671
+ self.encodekey = encodekey
672
+ self.decodekey = decodekey
673
+ self.encodevalue = encodevalue
674
+ self.decodevalue = decodevalue
675
+ self._data = data
676
+
677
+ def __getitem__(self, key):
678
+ try:
679
+ value = self._data[self.encodekey(key)]
680
+ except KeyError:
681
+ # raise KeyError with the original key value
682
+ raise KeyError(key) from None
683
+ return self.decodevalue(value)
684
+
685
+ def __setitem__(self, key, value):
686
+ key = self.encodekey(key)
687
+ value = self.encodevalue(value)
688
+ putenv(key, value)
689
+ self._data[key] = value
690
+
691
+ def __delitem__(self, key):
692
+ encodedkey = self.encodekey(key)
693
+ unsetenv(encodedkey)
694
+ try:
695
+ del self._data[encodedkey]
696
+ except KeyError:
697
+ # raise KeyError with the original key value
698
+ raise KeyError(key) from None
699
+
700
+ def __iter__(self):
701
+ # list() from dict object is an atomic operation
702
+ keys = list(self._data)
703
+ for key in keys:
704
+ yield self.decodekey(key)
705
+
706
+ def __len__(self):
707
+ return len(self._data)
708
+
709
+ def __repr__(self):
710
+ return 'environ({{{}}})'.format(', '.join(
711
+ ('{!r}: {!r}'.format(self.decodekey(key), self.decodevalue(value))
712
+ for key, value in self._data.items())))
713
+
714
+ def copy(self):
715
+ return dict(self)
716
+
717
+ def setdefault(self, key, value):
718
+ if key not in self:
719
+ self[key] = value
720
+ return self[key]
721
+
722
+ def __ior__(self, other):
723
+ self.update(other)
724
+ return self
725
+
726
+ def __or__(self, other):
727
+ if not isinstance(other, Mapping):
728
+ return NotImplemented
729
+ new = dict(self)
730
+ new.update(other)
731
+ return new
732
+
733
+ def __ror__(self, other):
734
+ if not isinstance(other, Mapping):
735
+ return NotImplemented
736
+ new = dict(other)
737
+ new.update(self)
738
+ return new
739
+
740
+ def _createenviron():
741
+ if name == 'nt':
742
+ # Where Env Var Names Must Be UPPERCASE
743
+ def check_str(value):
744
+ if not isinstance(value, str):
745
+ raise TypeError("str expected, not %s" % type(value).__name__)
746
+ return value
747
+ encode = check_str
748
+ decode = str
749
+ def encodekey(key):
750
+ return encode(key).upper()
751
+ data = {}
752
+ for key, value in environ.items():
753
+ data[encodekey(key)] = value
754
+ else:
755
+ # Where Env Var Names Can Be Mixed Case
756
+ encoding = sys.getfilesystemencoding()
757
+ def encode(value):
758
+ if not isinstance(value, str):
759
+ raise TypeError("str expected, not %s" % type(value).__name__)
760
+ return value.encode(encoding, 'surrogateescape')
761
+ def decode(value):
762
+ return value.decode(encoding, 'surrogateescape')
763
+ encodekey = encode
764
+ data = environ
765
+ return _Environ(data,
766
+ encodekey, decode,
767
+ encode, decode)
768
+
769
+ # unicode environ
770
+ environ = _createenviron()
771
+ del _createenviron
772
+
773
+
774
+ def getenv(key, default=None):
775
+ """Get an environment variable, return None if it doesn't exist.
776
+ The optional second argument can specify an alternate default.
777
+ key, default and the result are str."""
778
+ return environ.get(key, default)
779
+
780
+ supports_bytes_environ = (name != 'nt')
781
+ __all__.extend(("getenv", "supports_bytes_environ"))
782
+
783
+ if supports_bytes_environ:
784
+ def _check_bytes(value):
785
+ if not isinstance(value, bytes):
786
+ raise TypeError("bytes expected, not %s" % type(value).__name__)
787
+ return value
788
+
789
+ # bytes environ
790
+ environb = _Environ(environ._data,
791
+ _check_bytes, bytes,
792
+ _check_bytes, bytes)
793
+ del _check_bytes
794
+
795
+ def getenvb(key, default=None):
796
+ """Get an environment variable, return None if it doesn't exist.
797
+ The optional second argument can specify an alternate default.
798
+ key, default and the result are bytes."""
799
+ return environb.get(key, default)
800
+
801
+ __all__.extend(("environb", "getenvb"))
802
+
803
+ def _fscodec():
804
+ encoding = sys.getfilesystemencoding()
805
+ errors = sys.getfilesystemencodeerrors()
806
+
807
+ def fsencode(filename):
808
+ """Encode filename (an os.PathLike, bytes, or str) to the filesystem
809
+ encoding with 'surrogateescape' error handler, return bytes unchanged.
810
+ On Windows, use 'strict' error handler if the file system encoding is
811
+ 'mbcs' (which is the default encoding).
812
+ """
813
+ filename = fspath(filename) # Does type-checking of `filename`.
814
+ if isinstance(filename, str):
815
+ return filename.encode(encoding, errors)
816
+ else:
817
+ return filename
818
+
819
+ def fsdecode(filename):
820
+ """Decode filename (an os.PathLike, bytes, or str) from the filesystem
821
+ encoding with 'surrogateescape' error handler, return str unchanged. On
822
+ Windows, use 'strict' error handler if the file system encoding is
823
+ 'mbcs' (which is the default encoding).
824
+ """
825
+ filename = fspath(filename) # Does type-checking of `filename`.
826
+ if isinstance(filename, bytes):
827
+ return filename.decode(encoding, errors)
828
+ else:
829
+ return filename
830
+
831
+ return fsencode, fsdecode
832
+
833
+ fsencode, fsdecode = _fscodec()
834
+ del _fscodec
835
+
836
+ # Supply spawn*() (probably only for Unix)
837
+ if _exists("fork") and not _exists("spawnv") and _exists("execv"):
838
+
839
+ P_WAIT = 0
840
+ P_NOWAIT = P_NOWAITO = 1
841
+
842
+ __all__.extend(["P_WAIT", "P_NOWAIT", "P_NOWAITO"])
843
+
844
+ # XXX Should we support P_DETACH? I suppose it could fork()**2
845
+ # and close the std I/O streams. Also, P_OVERLAY is the same
846
+ # as execv*()?
847
+
848
+ def _spawnvef(mode, file, args, env, func):
849
+ # Internal helper; func is the exec*() function to use
850
+ if not isinstance(args, (tuple, list)):
851
+ raise TypeError('argv must be a tuple or a list')
852
+ if not args or not args[0]:
853
+ raise ValueError('argv first element cannot be empty')
854
+ pid = fork()
855
+ if not pid:
856
+ # Child
857
+ try:
858
+ if env is None:
859
+ func(file, args)
860
+ else:
861
+ func(file, args, env)
862
+ except:
863
+ _exit(127)
864
+ else:
865
+ # Parent
866
+ if mode == P_NOWAIT:
867
+ return pid # Caller is responsible for waiting!
868
+ while 1:
869
+ wpid, sts = waitpid(pid, 0)
870
+ if WIFSTOPPED(sts):
871
+ continue
872
+
873
+ return waitstatus_to_exitcode(sts)
874
+
875
+ def spawnv(mode, file, args):
876
+ """spawnv(mode, file, args) -> integer
877
+
878
+ Execute file with arguments from args in a subprocess.
879
+ If mode == P_NOWAIT return the pid of the process.
880
+ If mode == P_WAIT return the process's exit code if it exits normally;
881
+ otherwise return -SIG, where SIG is the signal that killed it. """
882
+ return _spawnvef(mode, file, args, None, execv)
883
+
884
+ def spawnve(mode, file, args, env):
885
+ """spawnve(mode, file, args, env) -> integer
886
+
887
+ Execute file with arguments from args in a subprocess with the
888
+ specified environment.
889
+ If mode == P_NOWAIT return the pid of the process.
890
+ If mode == P_WAIT return the process's exit code if it exits normally;
891
+ otherwise return -SIG, where SIG is the signal that killed it. """
892
+ return _spawnvef(mode, file, args, env, execve)
893
+
894
+ # Note: spawnvp[e] isn't currently supported on Windows
895
+
896
+ def spawnvp(mode, file, args):
897
+ """spawnvp(mode, file, args) -> integer
898
+
899
+ Execute file (which is looked for along $PATH) with arguments from
900
+ args in a subprocess.
901
+ If mode == P_NOWAIT return the pid of the process.
902
+ If mode == P_WAIT return the process's exit code if it exits normally;
903
+ otherwise return -SIG, where SIG is the signal that killed it. """
904
+ return _spawnvef(mode, file, args, None, execvp)
905
+
906
+ def spawnvpe(mode, file, args, env):
907
+ """spawnvpe(mode, file, args, env) -> integer
908
+
909
+ Execute file (which is looked for along $PATH) with arguments from
910
+ args in a subprocess with the supplied environment.
911
+ If mode == P_NOWAIT return the pid of the process.
912
+ If mode == P_WAIT return the process's exit code if it exits normally;
913
+ otherwise return -SIG, where SIG is the signal that killed it. """
914
+ return _spawnvef(mode, file, args, env, execvpe)
915
+
916
+
917
+ __all__.extend(["spawnv", "spawnve", "spawnvp", "spawnvpe"])
918
+
919
+
920
+ if _exists("spawnv"):
921
+ # These aren't supplied by the basic Windows code
922
+ # but can be easily implemented in Python
923
+
924
+ def spawnl(mode, file, *args):
925
+ """spawnl(mode, file, *args) -> integer
926
+
927
+ Execute file with arguments from args in a subprocess.
928
+ If mode == P_NOWAIT return the pid of the process.
929
+ If mode == P_WAIT return the process's exit code if it exits normally;
930
+ otherwise return -SIG, where SIG is the signal that killed it. """
931
+ return spawnv(mode, file, args)
932
+
933
+ def spawnle(mode, file, *args):
934
+ """spawnle(mode, file, *args, env) -> integer
935
+
936
+ Execute file with arguments from args in a subprocess with the
937
+ supplied environment.
938
+ If mode == P_NOWAIT return the pid of the process.
939
+ If mode == P_WAIT return the process's exit code if it exits normally;
940
+ otherwise return -SIG, where SIG is the signal that killed it. """
941
+ env = args[-1]
942
+ return spawnve(mode, file, args[:-1], env)
943
+
944
+
945
+ __all__.extend(["spawnl", "spawnle"])
946
+
947
+
948
+ if _exists("spawnvp"):
949
+ # At the moment, Windows doesn't implement spawnvp[e],
950
+ # so it won't have spawnlp[e] either.
951
+ def spawnlp(mode, file, *args):
952
+ """spawnlp(mode, file, *args) -> integer
953
+
954
+ Execute file (which is looked for along $PATH) with arguments from
955
+ args in a subprocess with the supplied environment.
956
+ If mode == P_NOWAIT return the pid of the process.
957
+ If mode == P_WAIT return the process's exit code if it exits normally;
958
+ otherwise return -SIG, where SIG is the signal that killed it. """
959
+ return spawnvp(mode, file, args)
960
+
961
+ def spawnlpe(mode, file, *args):
962
+ """spawnlpe(mode, file, *args, env) -> integer
963
+
964
+ Execute file (which is looked for along $PATH) with arguments from
965
+ args in a subprocess with the supplied environment.
966
+ If mode == P_NOWAIT return the pid of the process.
967
+ If mode == P_WAIT return the process's exit code if it exits normally;
968
+ otherwise return -SIG, where SIG is the signal that killed it. """
969
+ env = args[-1]
970
+ return spawnvpe(mode, file, args[:-1], env)
971
+
972
+
973
+ __all__.extend(["spawnlp", "spawnlpe"])
974
+
975
+
976
+ # Supply os.popen()
977
+ def popen(cmd, mode="r", buffering=-1):
978
+ if not isinstance(cmd, str):
979
+ raise TypeError("invalid cmd type (%s, expected string)" % type(cmd))
980
+ if mode not in ("r", "w"):
981
+ raise ValueError("invalid mode %r" % mode)
982
+ if buffering == 0 or buffering is None:
983
+ raise ValueError("popen() does not support unbuffered streams")
984
+ import subprocess, io
985
+ if mode == "r":
986
+ proc = subprocess.Popen(cmd,
987
+ shell=True,
988
+ stdout=subprocess.PIPE,
989
+ bufsize=buffering)
990
+ return _wrap_close(io.TextIOWrapper(proc.stdout), proc)
991
+ else:
992
+ proc = subprocess.Popen(cmd,
993
+ shell=True,
994
+ stdin=subprocess.PIPE,
995
+ bufsize=buffering)
996
+ return _wrap_close(io.TextIOWrapper(proc.stdin), proc)
997
+
998
+ # Helper for popen() -- a proxy for a file whose close waits for the process
999
+ class _wrap_close:
1000
+ def __init__(self, stream, proc):
1001
+ self._stream = stream
1002
+ self._proc = proc
1003
+ def close(self):
1004
+ self._stream.close()
1005
+ returncode = self._proc.wait()
1006
+ if returncode == 0:
1007
+ return None
1008
+ if name == 'nt':
1009
+ return returncode
1010
+ else:
1011
+ return returncode << 8 # Shift left to match old behavior
1012
+ def __enter__(self):
1013
+ return self
1014
+ def __exit__(self, *args):
1015
+ self.close()
1016
+ def __getattr__(self, name):
1017
+ return getattr(self._stream, name)
1018
+ def __iter__(self):
1019
+ return iter(self._stream)
1020
+
1021
+ # Supply os.fdopen()
1022
+ def fdopen(fd, *args, **kwargs):
1023
+ if not isinstance(fd, int):
1024
+ raise TypeError("invalid fd type (%s, expected integer)" % type(fd))
1025
+ import io
1026
+ return io.open(fd, *args, **kwargs)
1027
+
1028
+
1029
+ # For testing purposes, make sure the function is available when the C
1030
+ # implementation exists.
1031
+ def _fspath(path):
1032
+ """Return the path representation of a path-like object.
1033
+
1034
+ If str or bytes is passed in, it is returned unchanged. Otherwise the
1035
+ os.PathLike interface is used to get the path representation. If the
1036
+ path representation is not str or bytes, TypeError is raised. If the
1037
+ provided path is not str, bytes, or os.PathLike, TypeError is raised.
1038
+ """
1039
+ if isinstance(path, (str, bytes)):
1040
+ return path
1041
+
1042
+ # Work from the object's type to match method resolution of other magic
1043
+ # methods.
1044
+ path_type = type(path)
1045
+ try:
1046
+ path_repr = path_type.__fspath__(path)
1047
+ except AttributeError:
1048
+ if hasattr(path_type, '__fspath__'):
1049
+ raise
1050
+ else:
1051
+ raise TypeError("expected str, bytes or os.PathLike object, "
1052
+ "not " + path_type.__name__)
1053
+ if isinstance(path_repr, (str, bytes)):
1054
+ return path_repr
1055
+ else:
1056
+ raise TypeError("expected {}.__fspath__() to return str or bytes, "
1057
+ "not {}".format(path_type.__name__,
1058
+ type(path_repr).__name__))
1059
+
1060
+ # If there is no C implementation, make the pure Python version the
1061
+ # implementation as transparently as possible.
1062
+ if not _exists('fspath'):
1063
+ fspath = _fspath
1064
+ fspath.__name__ = "fspath"
1065
+
1066
+
1067
+ class PathLike(abc.ABC):
1068
+
1069
+ """Abstract base class for implementing the file system path protocol."""
1070
+
1071
+ @abc.abstractmethod
1072
+ def __fspath__(self):
1073
+ """Return the file system path representation of the object."""
1074
+ raise NotImplementedError
1075
+
1076
+ @classmethod
1077
+ def __subclasshook__(cls, subclass):
1078
+ if cls is PathLike:
1079
+ return _check_methods(subclass, '__fspath__')
1080
+ return NotImplemented
1081
+
1082
+ __class_getitem__ = classmethod(GenericAlias)
1083
+
1084
+
1085
+ if name == 'nt':
1086
+ class _AddedDllDirectory:
1087
+ def __init__(self, path, cookie, remove_dll_directory):
1088
+ self.path = path
1089
+ self._cookie = cookie
1090
+ self._remove_dll_directory = remove_dll_directory
1091
+ def close(self):
1092
+ self._remove_dll_directory(self._cookie)
1093
+ self.path = None
1094
+ def __enter__(self):
1095
+ return self
1096
+ def __exit__(self, *args):
1097
+ self.close()
1098
+ def __repr__(self):
1099
+ if self.path:
1100
+ return "<AddedDllDirectory({!r})>".format(self.path)
1101
+ return "<AddedDllDirectory()>"
1102
+
1103
+ def add_dll_directory(path):
1104
+ """Add a path to the DLL search path.
1105
+
1106
+ This search path is used when resolving dependencies for imported
1107
+ extension modules (the module itself is resolved through sys.path),
1108
+ and also by ctypes.
1109
+
1110
+ Remove the directory by calling close() on the returned object or
1111
+ using it in a with statement.
1112
+ """
1113
+ import nt
1114
+ cookie = nt._add_dll_directory(path)
1115
+ return _AddedDllDirectory(
1116
+ path,
1117
+ cookie,
1118
+ nt._remove_dll_directory
1119
+ )
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio[mcp]
2
+ textblob
streamlit_app.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #########################################################################
2
+ # Copyright (C) #
3
+ # 2025-June Sen Li (Sen.Li.Sprout@gmail.com) #
4
+ # Permission given to modify the code only for Non-Profit Research #
5
+ # as long as you keep this declaration at the top #
6
+ #########################################################################
7
+
8
+ import gradio as gr
9
+ import textblob
10
+ import os
11
+
12
+ def func_SentimentAnalysis(str_tgtText_2_Analyze: str) -> dict:
13
+ """ Analyze the sentiment of the given text.
14
+ Args:
15
+ str_tgtText_2_Analyze (str): The text to analyze
16
+
17
+ Returns:
18
+ dict_SentimentAnalysis: { polarity [-1, 1],
19
+ subjectivity objective [0, 1] subjective
20
+ assessment ["positive", "neutral", "negative"]
21
+ }
22
+ """
23
+ textBlob_tgtText = textblob.TextBlob(str_tgtText_2_Analyze)
24
+ sentiment_tgtText = textBlob_tgtText.sentiment
25
+ dict_SentimentAnalysis = {
26
+ "polarity": round(sentiment_tgtText.polarity, 2),
27
+ "subjectivity": round(sentiment_tgtText.subjectivity, 2),
28
+ "assessment": "positive" if sentiment_tgtText.polarity > 0 \
29
+ else "negative" if sentiment_tgtText.polarity < 0 else "neutral"
30
+ }
31
+ return dict_SentimentAnalysis
32
+
33
+
34
+
35
+
36
+ # Launch the interface and MCP server
37
+ if __name__ == "__main__":
38
+ print(f"os.getcwd() = {os.getcwd()}")
39
+ os.system(f"echo ls -al {os.getcwd()} && ls -al {os.getcwd()}")
40
+ os.system(f"echo ls -al /app/../: && ls -al /app/../")
41
+ # os.system(f"echo ls -al /.gradio/: && ls -al /.gradio/")
42
+ os.system(f"echo ls -al /app/: && ls -al /app/")
43
+ os.system(f"echo ls -al /home/: && ls -al /home/")
44
+ os.system(f"echo ls -al /app/src/: && ls -al /app/src/")
45
+
46
+ # Create the Gradio interface
47
+ grInterface_SentimentAnalysis = gr.Interface(
48
+ fn = func_SentimentAnalysis,
49
+ inputs = gr.Textbox(placeholder="Enter text to analyze..."),
50
+ outputs = gr.JSON(),
51
+ title = "Text Sentiment Analysis",
52
+ description = "Analyze the sentiment of text using TextBlob"
53
+ )
54
+
55
+
56
+ # MCP Schema: Visit http://localhost:7860/gradio_api/mcp/schema
57
+ # MCP Server:
58
+ # Setting mcp_server=True enables the MCP server, enable using the environment variable:
59
+ # export GRADIO_MCP_SERVER=True # http://localhost:7860/gradio_api/mcp/sse
60
+
61
+ grInterface_SentimentAnalysis.launch(share=True) # mcp_server=True, share=True)
62
+
63
+ os.system(f"echo ls -al /app/flagged/: && ls -al /app/flagged/")
64
+ # os.system(f"echo ls -al /.gradio/flagged/: && ls -al /.gradio/flagged/")