pybox-nlp 0.1.0__py3-none-any.whl
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.
- pybox/__init__.py +57 -0
- pybox/core.py +163 -0
- pybox/programs/__init__.py +6 -0
- pybox/programs/eight.py +27 -0
- pybox/programs/eleven.py +36 -0
- pybox/programs/five.py +30 -0
- pybox/programs/four.py +40 -0
- pybox/programs/nine.py +41 -0
- pybox/programs/one.py +27 -0
- pybox/programs/seven.py +37 -0
- pybox/programs/six.py +59 -0
- pybox/programs/ten.py +29 -0
- pybox/programs/three.py +30 -0
- pybox/programs/two.py +23 -0
- pybox/py.typed +1 -0
- pybox_nlp-0.1.0.dist-info/METADATA +357 -0
- pybox_nlp-0.1.0.dist-info/RECORD +20 -0
- pybox_nlp-0.1.0.dist-info/WHEEL +5 -0
- pybox_nlp-0.1.0.dist-info/licenses/LICENSE +21 -0
- pybox_nlp-0.1.0.dist-info/top_level.txt +1 -0
pybox/__init__.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pybox: Bundles 11 Python programs into one installable package with source inspection and export capabilities.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Tuple
|
|
8
|
+
|
|
9
|
+
from .core import Program
|
|
10
|
+
|
|
11
|
+
__version__ = "0.1.0"
|
|
12
|
+
|
|
13
|
+
PROGRAM_NAMES: Tuple[str, ...] = (
|
|
14
|
+
"one",
|
|
15
|
+
"two",
|
|
16
|
+
"three",
|
|
17
|
+
"four",
|
|
18
|
+
"five",
|
|
19
|
+
"six",
|
|
20
|
+
"seven",
|
|
21
|
+
"eight",
|
|
22
|
+
"nine",
|
|
23
|
+
"ten",
|
|
24
|
+
"eleven",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
PROGRAMS: Dict[str, Program] = {name: Program(name) for name in PROGRAM_NAMES}
|
|
28
|
+
|
|
29
|
+
one: Program = PROGRAMS["one"]
|
|
30
|
+
two: Program = PROGRAMS["two"]
|
|
31
|
+
three: Program = PROGRAMS["three"]
|
|
32
|
+
four: Program = PROGRAMS["four"]
|
|
33
|
+
five: Program = PROGRAMS["five"]
|
|
34
|
+
six: Program = PROGRAMS["six"]
|
|
35
|
+
seven: Program = PROGRAMS["seven"]
|
|
36
|
+
eight: Program = PROGRAMS["eight"]
|
|
37
|
+
nine: Program = PROGRAMS["nine"]
|
|
38
|
+
ten: Program = PROGRAMS["ten"]
|
|
39
|
+
eleven: Program = PROGRAMS["eleven"]
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"Program",
|
|
43
|
+
"PROGRAM_NAMES",
|
|
44
|
+
"PROGRAMS",
|
|
45
|
+
"__version__",
|
|
46
|
+
"one",
|
|
47
|
+
"two",
|
|
48
|
+
"three",
|
|
49
|
+
"four",
|
|
50
|
+
"five",
|
|
51
|
+
"six",
|
|
52
|
+
"seven",
|
|
53
|
+
"eight",
|
|
54
|
+
"nine",
|
|
55
|
+
"ten",
|
|
56
|
+
"eleven",
|
|
57
|
+
]
|
pybox/core.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core implementation of the PyBox Program abstraction.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import tempfile
|
|
10
|
+
from typing import Union
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Program:
|
|
14
|
+
"""
|
|
15
|
+
Represents an individual bundled Python program within pybox.
|
|
16
|
+
|
|
17
|
+
Provides safe inspection of original source code without executing it,
|
|
18
|
+
as well as export functionality to save standalone .py files.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, identifier: str) -> None:
|
|
22
|
+
if not identifier or not isinstance(identifier, str):
|
|
23
|
+
raise ValueError(f"Program identifier must be a non-empty string, got: {identifier!r}")
|
|
24
|
+
self._identifier: str = identifier
|
|
25
|
+
self._filename: str = f"{identifier}.py"
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def identifier(self) -> str:
|
|
29
|
+
"""The identifier of this program (e.g., 'one', 'two')."""
|
|
30
|
+
return self._identifier
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def filename(self) -> str:
|
|
34
|
+
"""The default filename for this program (e.g., 'one.py')."""
|
|
35
|
+
return self._filename
|
|
36
|
+
|
|
37
|
+
def _read_source(self) -> str:
|
|
38
|
+
"""
|
|
39
|
+
Locate and read the packaged source file using UTF-8 encoding.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
FileNotFoundError: If the source file cannot be found in the package.
|
|
43
|
+
OSError: If an error occurs while reading the file.
|
|
44
|
+
"""
|
|
45
|
+
# Try importlib.resources (Python 3.9+)
|
|
46
|
+
try:
|
|
47
|
+
from importlib import resources
|
|
48
|
+
traversable = resources.files("pybox.programs").joinpath(self._filename)
|
|
49
|
+
# traversable.is_file() might raise AttributeError on older backports, so check safely
|
|
50
|
+
try:
|
|
51
|
+
is_file = traversable.is_file()
|
|
52
|
+
except AttributeError:
|
|
53
|
+
is_file = True
|
|
54
|
+
|
|
55
|
+
if is_file:
|
|
56
|
+
return traversable.read_text(encoding="utf-8")
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
# Fallback to direct filesystem resolution relative to this file
|
|
61
|
+
fallback_path = Path(__file__).resolve().parent / "programs" / self._filename
|
|
62
|
+
if fallback_path.is_file():
|
|
63
|
+
return fallback_path.read_text(encoding="utf-8")
|
|
64
|
+
|
|
65
|
+
raise FileNotFoundError(
|
|
66
|
+
f"Packaged source file for program '{self._identifier}' "
|
|
67
|
+
f"('{self._filename}') was not found in pybox.programs."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def code(self) -> str:
|
|
72
|
+
"""The complete original source code of the program as a string."""
|
|
73
|
+
return self._read_source()
|
|
74
|
+
|
|
75
|
+
def __str__(self) -> str:
|
|
76
|
+
"""Return the complete source code when the program is converted to str or printed."""
|
|
77
|
+
return self.code
|
|
78
|
+
|
|
79
|
+
def __repr__(self) -> str:
|
|
80
|
+
"""Return a helpful developer representation."""
|
|
81
|
+
return f"Program({self._identifier!r})"
|
|
82
|
+
|
|
83
|
+
def save(
|
|
84
|
+
self,
|
|
85
|
+
destination: Union[str, Path, None] = None,
|
|
86
|
+
*,
|
|
87
|
+
overwrite: bool = False,
|
|
88
|
+
) -> Path:
|
|
89
|
+
"""
|
|
90
|
+
Export the program's source code into a standalone Python file.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
destination: Optional file path or directory. Defaults to '<identifier>.py'
|
|
94
|
+
in the current working directory.
|
|
95
|
+
overwrite: If False (default), raises FileExistsError if the destination exists.
|
|
96
|
+
If True, safely replaces the destination file.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
The resolved Path to the saved Python file.
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
FileExistsError: If destination exists and overwrite is False.
|
|
103
|
+
IsADirectoryError: If destination is an existing directory and no filename is given.
|
|
104
|
+
OSError: If an error occurs creating directories or writing the file.
|
|
105
|
+
"""
|
|
106
|
+
source_code = self.code
|
|
107
|
+
|
|
108
|
+
if destination is None:
|
|
109
|
+
dest_path = Path.cwd() / self._filename
|
|
110
|
+
else:
|
|
111
|
+
dest_path = Path(destination)
|
|
112
|
+
dest_str = str(destination)
|
|
113
|
+
if dest_str.endswith(("/", "\\")) or (dest_path.is_dir() and not dest_path.suffix):
|
|
114
|
+
dest_path = dest_path / self._filename
|
|
115
|
+
|
|
116
|
+
# Refuse to overwrite directory
|
|
117
|
+
if dest_path.is_dir():
|
|
118
|
+
raise IsADirectoryError(
|
|
119
|
+
f"Destination path '{dest_path}' is an existing directory. "
|
|
120
|
+
"Cannot overwrite a directory with a file."
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Strict overwrite check
|
|
124
|
+
if dest_path.exists() and not overwrite:
|
|
125
|
+
raise FileExistsError(
|
|
126
|
+
f"Destination file '{dest_path}' already exists. "
|
|
127
|
+
"Pass 'overwrite=True' to allow overwriting."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
parent_dir = dest_path.parent
|
|
131
|
+
try:
|
|
132
|
+
parent_dir.mkdir(parents=True, exist_ok=True)
|
|
133
|
+
except OSError as e:
|
|
134
|
+
raise OSError(f"Failed to create directory '{parent_dir}': {e}") from e
|
|
135
|
+
|
|
136
|
+
# Safe atomic write via temporary file in the same target directory
|
|
137
|
+
temp_file_path: Path | None = None
|
|
138
|
+
try:
|
|
139
|
+
with tempfile.NamedTemporaryFile(
|
|
140
|
+
mode="w",
|
|
141
|
+
encoding="utf-8",
|
|
142
|
+
dir=parent_dir,
|
|
143
|
+
delete=False,
|
|
144
|
+
prefix=f".tmp_{dest_path.stem}_",
|
|
145
|
+
suffix=".tmp",
|
|
146
|
+
) as tmp:
|
|
147
|
+
temp_file_path = Path(tmp.name)
|
|
148
|
+
tmp.write(source_code)
|
|
149
|
+
tmp.flush()
|
|
150
|
+
os.fsync(tmp.fileno())
|
|
151
|
+
|
|
152
|
+
# Atomic replace
|
|
153
|
+
os.replace(temp_file_path, dest_path)
|
|
154
|
+
temp_file_path = None
|
|
155
|
+
except Exception as e:
|
|
156
|
+
if temp_file_path is not None and temp_file_path.exists():
|
|
157
|
+
try:
|
|
158
|
+
temp_file_path.unlink()
|
|
159
|
+
except OSError:
|
|
160
|
+
pass
|
|
161
|
+
raise OSError(f"Failed to export source code to '{dest_path}': {e}") from e
|
|
162
|
+
|
|
163
|
+
return dest_path.resolve()
|
pybox/programs/eight.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import nltk
|
|
2
|
+
from nltk.corpus import wordnet as wn
|
|
3
|
+
from nltk.wsd import lesk
|
|
4
|
+
# Download necessary resources
|
|
5
|
+
nltk.download('wordnet')
|
|
6
|
+
nltk.download('punkt')
|
|
7
|
+
nltk.download('omw-1.4')
|
|
8
|
+
def perform_wsd(sentences):
|
|
9
|
+
for sentence in sentences:
|
|
10
|
+
tokens = nltk.word_tokenize(sentence)
|
|
11
|
+
print(f"\nSentence: '{sentence}'")
|
|
12
|
+
for word in tokens:
|
|
13
|
+
synset = lesk(sentence, word)
|
|
14
|
+
if synset:
|
|
15
|
+
print(f"Word: {word}")
|
|
16
|
+
print(f"Sense: {synset.name()}")
|
|
17
|
+
print(f"Definition: {synset.definition()}")
|
|
18
|
+
else:
|
|
19
|
+
print(f"Word: {word}")
|
|
20
|
+
print("Sense: No disambiguation found")
|
|
21
|
+
# Example usage with three sentences
|
|
22
|
+
sentences = [
|
|
23
|
+
"The bank can guarantee deposits will eventually cover future tuition costs.",
|
|
24
|
+
"He went to the bank to deposit his money.",
|
|
25
|
+
"The river bank was full of blooming flowers."
|
|
26
|
+
]
|
|
27
|
+
perform_wsd(sentences)
|
pybox/programs/eleven.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import nltk
|
|
2
|
+
from nltk.featstruct import FeatStruct
|
|
3
|
+
# Define selectional restrictions for a predicate
|
|
4
|
+
selectional_restrictions = {
|
|
5
|
+
'eat': {'AGENT': {'ANIMATED': '+', 'HUMAN': '+'}, 'THEME': {'EDIBLE': '+'}},
|
|
6
|
+
'sleep': {'SUBJECT': {'ANIMATED': '+', 'HUMAN': '+'}}
|
|
7
|
+
}
|
|
8
|
+
# Define a feature structure for an entity
|
|
9
|
+
entity_feature_structure = FeatStruct(
|
|
10
|
+
ANIMATED='+',
|
|
11
|
+
HUMAN='+'
|
|
12
|
+
)
|
|
13
|
+
# Define a feature structure for a food item
|
|
14
|
+
food_feature_structure = FeatStruct(
|
|
15
|
+
EDIBLE='+'
|
|
16
|
+
)
|
|
17
|
+
# Function to check if a feature structure satisfies selectional restrictions for a predicate
|
|
18
|
+
def satisfies_selectional_restrictions(predicate, argument_feature_structure):
|
|
19
|
+
if predicate in selectional_restrictions:
|
|
20
|
+
for arg_name, restrictions in selectional_restrictions[predicate].items():
|
|
21
|
+
for feature, value in restrictions.items():
|
|
22
|
+
if value == '+':
|
|
23
|
+
if feature not in argument_feature_structure or argument_feature_structure[feature] != '+':
|
|
24
|
+
return False
|
|
25
|
+
elif value == '-':
|
|
26
|
+
if feature in argument_feature_structure and argument_feature_structure[feature] == '+':
|
|
27
|
+
return False
|
|
28
|
+
return True
|
|
29
|
+
# Example usage
|
|
30
|
+
predicate = 'eat'
|
|
31
|
+
agent = entity_feature_structure
|
|
32
|
+
theme = food_feature_structure
|
|
33
|
+
if satisfies_selectional_restrictions(predicate, agent) and satisfies_selectional_restrictions(predicate, theme):
|
|
34
|
+
print(f"The feature structures satisfy selectional restrictions for the predicate '{predicate}'.")
|
|
35
|
+
else:
|
|
36
|
+
print(f"The feature structures do not satisfy selectional restrictions for the predicate '{predicate}'.")
|
pybox/programs/five.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import nltk
|
|
2
|
+
from nltk.corpus import treebank
|
|
3
|
+
# Download necessary resources
|
|
4
|
+
nltk.download('treebank')
|
|
5
|
+
nltk.download('punkt')
|
|
6
|
+
def analyze_sentential_components():
|
|
7
|
+
# Access the parsed sentences in the Penn Treebank
|
|
8
|
+
parsed_sentences = treebank.parsed_sents()
|
|
9
|
+
# Analyze a sample sentence
|
|
10
|
+
sample_sentence = parsed_sentences[0]
|
|
11
|
+
|
|
12
|
+
print("Sample sentence:")
|
|
13
|
+
print(" ".join(sample_sentence.leaves()))
|
|
14
|
+
print("\nParse tree:")
|
|
15
|
+
print(sample_sentence)
|
|
16
|
+
|
|
17
|
+
# Find and print specific components
|
|
18
|
+
print("\nNoun Phrases (NP):")
|
|
19
|
+
for subtree in sample_sentence.subtrees(filter=lambda t: t.label() == 'NP'):
|
|
20
|
+
print(" ".join(subtree.leaves()))
|
|
21
|
+
|
|
22
|
+
print("\nVerb Phrases (VP):")
|
|
23
|
+
for subtree in sample_sentence.subtrees(filter=lambda t: t.label() == 'VP'):
|
|
24
|
+
print(" ".join(subtree.leaves()))
|
|
25
|
+
|
|
26
|
+
print("\nPrepositional Phrases (PP):")
|
|
27
|
+
for subtree in sample_sentence.subtrees(filter=lambda t: t.label() == 'PP'):
|
|
28
|
+
print(" ".join(subtree.leaves()))
|
|
29
|
+
# Example usage
|
|
30
|
+
analyze_sentential_components()
|
pybox/programs/four.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import nltk
|
|
2
|
+
from collections import Counter, defaultdict
|
|
3
|
+
from nltk.util import ngrams
|
|
4
|
+
from nltk.tokenize import word_tokenize
|
|
5
|
+
import math
|
|
6
|
+
# Download necessary resources
|
|
7
|
+
nltk.download('punkt')
|
|
8
|
+
def generate_ngrams(text, n):
|
|
9
|
+
# Tokenize the text
|
|
10
|
+
tokens = word_tokenize(text.lower())
|
|
11
|
+
|
|
12
|
+
# Generate n-grams
|
|
13
|
+
n_grams = list(ngrams(tokens, n))
|
|
14
|
+
return n_grams
|
|
15
|
+
def calculate_ngram_probabilities(ngrams):
|
|
16
|
+
# Calculate frequencies of n-grams and (n-1)-grams
|
|
17
|
+
ngram_freq = Counter(ngrams)
|
|
18
|
+
n_minus_1_gram_freq = Counter([ngram[:-1] for ngram in ngrams])
|
|
19
|
+
|
|
20
|
+
# Calculate probabilities
|
|
21
|
+
ngram_probabilities = defaultdict(float)
|
|
22
|
+
for ngram in ngram_freq:
|
|
23
|
+
n_minus_1_gram = ngram[:-1]
|
|
24
|
+
ngram_probabilities[ngram] = ngram_freq[ngram] / n_minus_1_gram_freq[n_minus_1_gram]
|
|
25
|
+
|
|
26
|
+
return ngram_probabilities
|
|
27
|
+
def generate_bigram_language_model(text):
|
|
28
|
+
# Generate bigrams (2-grams)
|
|
29
|
+
bigrams = generate_ngrams(text, 2)
|
|
30
|
+
|
|
31
|
+
# Calculate bigram probabilities
|
|
32
|
+
bigram_probabilities = calculate_ngram_probabilities(bigrams)
|
|
33
|
+
|
|
34
|
+
return bigram_probabilities
|
|
35
|
+
# Example usage
|
|
36
|
+
text = "This is a simple example sentence for demonstrating a bigram language model. This example is simple."
|
|
37
|
+
bigram_model = generate_bigram_language_model(text)
|
|
38
|
+
# Print bigram probabilities
|
|
39
|
+
for bigram, prob in bigram_model.items():
|
|
40
|
+
print(f"Bigram: {bigram}, Probability: {prob:.4f}")
|
pybox/programs/nine.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import nltk
|
|
2
|
+
from nltk.corpus import wordnet as wn
|
|
3
|
+
from nltk.tokenize import word_tokenize
|
|
4
|
+
from nltk import pos_tag
|
|
5
|
+
# Download required nltk data
|
|
6
|
+
nltk.download('punkt')
|
|
7
|
+
nltk.download('averaged_perceptron_tagger')
|
|
8
|
+
nltk.download('wordnet')
|
|
9
|
+
# Define a simple feature structure
|
|
10
|
+
class FeatureStructure:
|
|
11
|
+
def __init__(self, word, pos, synsets):
|
|
12
|
+
self.word = word
|
|
13
|
+
self.pos = pos
|
|
14
|
+
self.synsets = synsets
|
|
15
|
+
def __repr__(self):
|
|
16
|
+
return f"FeatureStructure(word='{self.word}', pos='{self.pos}', synsets={self.synsets})"
|
|
17
|
+
# Function to identify semantic roles
|
|
18
|
+
def identify_semantic_roles(sentence):
|
|
19
|
+
tokens = word_tokenize(sentence)
|
|
20
|
+
tagged_tokens = pos_tag(tokens)
|
|
21
|
+
# Create feature structures for each word
|
|
22
|
+
features = []
|
|
23
|
+
for word, pos in tagged_tokens:
|
|
24
|
+
synsets = wn.synsets(word)
|
|
25
|
+
features.append(FeatureStructure(word, pos, synsets))
|
|
26
|
+
# Simple semantic role assignment based on POS tags
|
|
27
|
+
roles = []
|
|
28
|
+
for feature in features:
|
|
29
|
+
if feature.pos.startswith('VB'):
|
|
30
|
+
roles.append((feature.word, 'Action'))
|
|
31
|
+
elif feature.pos.startswith('NN'):
|
|
32
|
+
# This is a simplistic approach; normally semantic roles are determined
|
|
33
|
+
# based on syntactic structure and more complex rules
|
|
34
|
+
roles.append((feature.word, 'Agent' if 'nsubj' in pos else 'Patient'))
|
|
35
|
+
else:
|
|
36
|
+
roles.append((feature.word, 'None'))
|
|
37
|
+
return roles
|
|
38
|
+
# Example usage
|
|
39
|
+
sentence = "The cat chased the mouse"
|
|
40
|
+
roles = identify_semantic_roles(sentence)
|
|
41
|
+
print(roles)
|
pybox/programs/one.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
def count_words(sentence):
|
|
2
|
+
# Convert the sentence to lower case to make the counting case-insensitive
|
|
3
|
+
sentence = sentence.lower()
|
|
4
|
+
|
|
5
|
+
# Remove punctuation from the sentence
|
|
6
|
+
import string
|
|
7
|
+
translator = str.maketrans('', '', string.punctuation)
|
|
8
|
+
sentence = sentence.translate(translator)
|
|
9
|
+
|
|
10
|
+
# Split the sentence into words
|
|
11
|
+
words = sentence.split()
|
|
12
|
+
|
|
13
|
+
# Create a dictionary to count the words
|
|
14
|
+
word_count = {}
|
|
15
|
+
|
|
16
|
+
for word in words:
|
|
17
|
+
if word in word_count:
|
|
18
|
+
word_count[word] += 1
|
|
19
|
+
else:
|
|
20
|
+
word_count[word] = 1
|
|
21
|
+
|
|
22
|
+
return word_count
|
|
23
|
+
# Example usage
|
|
24
|
+
sentence = "This is a test. This test is only a test."
|
|
25
|
+
word_count = count_words(sentence)
|
|
26
|
+
for word, count in word_count.items():
|
|
27
|
+
print(f"{word}: {count}")
|
pybox/programs/seven.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import nltk
|
|
2
|
+
from nltk import CFG
|
|
3
|
+
from nltk.parse.generate import generate
|
|
4
|
+
# Define a small fragment of English using CFG
|
|
5
|
+
cfg_grammar = CFG.fromstring("""
|
|
6
|
+
S -> NP VP
|
|
7
|
+
NP -> Det N | Det Adj N | NP PP
|
|
8
|
+
VP -> V NP | VP PP
|
|
9
|
+
PP -> P NP
|
|
10
|
+
Det -> 'the' | 'a'
|
|
11
|
+
N -> 'dog' | 'cat' | 'park' | 'telescope'
|
|
12
|
+
Adj -> 'big' | 'small'
|
|
13
|
+
V -> 'saw' | 'chased'
|
|
14
|
+
P -> 'in' | 'with'
|
|
15
|
+
""")
|
|
16
|
+
# Initialize the parser
|
|
17
|
+
parser = nltk.ChartParser(cfg_grammar)
|
|
18
|
+
# Define some test sentences
|
|
19
|
+
test_sentences = [
|
|
20
|
+
"the dog saw a cat",
|
|
21
|
+
"a big dog chased the small cat",
|
|
22
|
+
"the cat saw the dog in the park",
|
|
23
|
+
"the dog saw the cat with the telescope"
|
|
24
|
+
]
|
|
25
|
+
# Parse and display the test sentences
|
|
26
|
+
for sentence in test_sentences:
|
|
27
|
+
print(f"Parsing sentence: '{sentence}'")
|
|
28
|
+
tokens = sentence.split()
|
|
29
|
+
for tree in parser.parse(tokens):
|
|
30
|
+
print(tree)
|
|
31
|
+
# Save the parse tree as text
|
|
32
|
+
with open("parse_tree.txt", "a") as f:
|
|
33
|
+
f.write(str(tree) + "\n")
|
|
34
|
+
# Generate some sentences from the CFG (optional)
|
|
35
|
+
print("\nGenerated sentences from the CFG:")
|
|
36
|
+
for sentence in generate(cfg_grammar, n=10):
|
|
37
|
+
print(' '.join(sentence))
|
pybox/programs/six.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import nltk
|
|
2
|
+
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
|
|
3
|
+
from nltk.corpus import stopwords
|
|
4
|
+
from nltk.stem import PorterStemmer
|
|
5
|
+
from nltk.stem import WordNetLemmatizer
|
|
6
|
+
from nltk.tokenize import word_tokenize
|
|
7
|
+
# Download necessary resources
|
|
8
|
+
nltk.download('punkt')
|
|
9
|
+
nltk.download('stopwords')
|
|
10
|
+
nltk.download('wordnet')
|
|
11
|
+
def preprocess_text(text):
|
|
12
|
+
# Tokenize the text
|
|
13
|
+
tokens = word_tokenize(text.lower())
|
|
14
|
+
|
|
15
|
+
# Remove stopwords
|
|
16
|
+
stop_words = set(stopwords.words('english'))
|
|
17
|
+
filtered_tokens = [token for token in tokens if token.isalnum() and token not in stop_words]
|
|
18
|
+
|
|
19
|
+
return filtered_tokens
|
|
20
|
+
def stem_tokens(tokens):
|
|
21
|
+
# Initialize the stemmer
|
|
22
|
+
stemmer = PorterStemmer()
|
|
23
|
+
stemmed_tokens = [stemmer.stem(token) for token in tokens]
|
|
24
|
+
return stemmed_tokens
|
|
25
|
+
def lemmatize_tokens(tokens):
|
|
26
|
+
# Initialize the lemmatizer
|
|
27
|
+
lemmatizer = WordNetLemmatizer()
|
|
28
|
+
lemmatized_tokens = [lemmatizer.lemmatize(token) for token in tokens]
|
|
29
|
+
return lemmatized_tokens
|
|
30
|
+
def extract_features(texts):
|
|
31
|
+
# Preprocess the text
|
|
32
|
+
preprocessed_texts = [" ".join(preprocess_text(text)) for text in texts]
|
|
33
|
+
|
|
34
|
+
# Extract features using CountVectorizer
|
|
35
|
+
count_vectorizer = CountVectorizer()
|
|
36
|
+
count_features = count_vectorizer.fit_transform(preprocessed_texts)
|
|
37
|
+
|
|
38
|
+
# Extract features using TfidfVectorizer
|
|
39
|
+
tfidf_vectorizer = TfidfVectorizer()
|
|
40
|
+
tfidf_features = tfidf_vectorizer.fit_transform(preprocessed_texts)
|
|
41
|
+
|
|
42
|
+
return count_features, tfidf_features, count_vectorizer, tfidf_vectorizer
|
|
43
|
+
# Example usage
|
|
44
|
+
texts = [
|
|
45
|
+
"This is a simple example sentence for demonstrating feature extraction.",
|
|
46
|
+
"Feature extraction involves various techniques like tokenization, stemming, and lemmatization.",
|
|
47
|
+
"Text preprocessing is essential for text analysis and machine learning."
|
|
48
|
+
]
|
|
49
|
+
# Extract features
|
|
50
|
+
count_features, tfidf_features, count_vectorizer, tfidf_vectorizer = extract_features(texts)
|
|
51
|
+
# Display the feature names and feature vectors
|
|
52
|
+
print("CountVectorizer Feature Names:")
|
|
53
|
+
print(count_vectorizer.get_feature_names_out())
|
|
54
|
+
print("\nCountVectorizer Feature Vectors:")
|
|
55
|
+
print(count_features.toarray())
|
|
56
|
+
print("\nTfidfVectorizer Feature Names:")
|
|
57
|
+
print(tfidf_vectorizer.get_feature_names_out())
|
|
58
|
+
print("\nTfidfVectorizer Feature Vectors:")
|
|
59
|
+
print(tfidf_features.toarray())
|
pybox/programs/ten.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import re
|
|
2
|
+
def translate_formula(formula):
|
|
3
|
+
formula = formula.strip()
|
|
4
|
+
# Universal quantifier
|
|
5
|
+
if formula.startswith("forall"):
|
|
6
|
+
match = re.match(r"forall\s+(\w+)\s*\((\w+)\((\w+)\)\s*->\s*(\w+)\((\w+)\)\)", formula)
|
|
7
|
+
if match:
|
|
8
|
+
var, pred1, _, pred2, _ = match.groups()
|
|
9
|
+
return f"Every {pred1.lower()} is {pred2.lower()}."
|
|
10
|
+
# Existential quantifier with AND
|
|
11
|
+
if formula.startswith("exists"):
|
|
12
|
+
match = re.match(
|
|
13
|
+
r"exists\s+(\w+)\s*\((\w+)\((\w+)\)\s*&\s*(\w+)\((\w+)\)\)",
|
|
14
|
+
formula
|
|
15
|
+
)
|
|
16
|
+
if match:
|
|
17
|
+
var, pred1, _, pred2, _ = match.groups()
|
|
18
|
+
return f"There exists a {pred1.lower()} who is {pred2.lower()}."
|
|
19
|
+
return "Formula format not recognized."
|
|
20
|
+
# Test formulas
|
|
21
|
+
formulas = [
|
|
22
|
+
"forall x (Dog(x) -> Animal(x))",
|
|
23
|
+
"forall x (Human(x) -> Mortal(x))",
|
|
24
|
+
"exists x (Student(x) & Smart(x))"
|
|
25
|
+
]
|
|
26
|
+
for formula in formulas:
|
|
27
|
+
print("Predicate Formula:", formula)
|
|
28
|
+
print("Translation:", translate_formula(formula))
|
|
29
|
+
print()
|
pybox/programs/three.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import nltk
|
|
2
|
+
from nltk.corpus import stopwords
|
|
3
|
+
from nltk.tokenize import word_tokenize
|
|
4
|
+
from nltk.stem import WordNetLemmatizer
|
|
5
|
+
# Download necessary resources
|
|
6
|
+
nltk.download('punkt')
|
|
7
|
+
nltk.download('wordnet')
|
|
8
|
+
nltk.download('stopwords')
|
|
9
|
+
def morphological_analysis_and_remove_stopwords(sentence):
|
|
10
|
+
# Tokenize the sentence
|
|
11
|
+
words = word_tokenize(sentence)
|
|
12
|
+
|
|
13
|
+
# Initialize the lemmatizer
|
|
14
|
+
lemmatizer = WordNetLemmatizer()
|
|
15
|
+
|
|
16
|
+
# Perform lemmatization
|
|
17
|
+
lemmatized_words = [lemmatizer.lemmatize(word) for word in words]
|
|
18
|
+
|
|
19
|
+
# Retrieve the list of stopwords
|
|
20
|
+
stop_words = set(stopwords.words('english'))
|
|
21
|
+
|
|
22
|
+
# Remove stopwords
|
|
23
|
+
filtered_words = [word for word in lemmatized_words if word.lower() not in stop_words]
|
|
24
|
+
|
|
25
|
+
return filtered_words
|
|
26
|
+
# Example usage
|
|
27
|
+
sentence = "This is a simple example sentence for demonstrating morphological analysis and removing stopwords."
|
|
28
|
+
filtered_words = morphological_analysis_and_remove_stopwords(sentence)
|
|
29
|
+
print("Original Sentence:", sentence)
|
|
30
|
+
print("Filtered Words:", filtered_words)
|
pybox/programs/two.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import re
|
|
2
|
+
def demonstrate_regex_operations(sentence):
|
|
3
|
+
# Example usage of re.match()
|
|
4
|
+
# Match checks for a pattern only at the beginning of the string
|
|
5
|
+
match_result = re.match(r'\w+', sentence)
|
|
6
|
+
if match_result:
|
|
7
|
+
print(f"Match: '{match_result.group()}' found at the beginning of the sentence")
|
|
8
|
+
else:
|
|
9
|
+
print("No match found at the beginning of the sentence")
|
|
10
|
+
# Example usage of re.search()
|
|
11
|
+
# Search checks for a pattern anywhere in the string
|
|
12
|
+
search_result = re.search(r'\bis\b', sentence)
|
|
13
|
+
if search_result:
|
|
14
|
+
print(f"Search: '{search_result.group()}' found in the sentence")
|
|
15
|
+
else:
|
|
16
|
+
print("No search result found in the sentence")
|
|
17
|
+
# Example usage of re.sub()
|
|
18
|
+
# Sub replaces occurrences of the pattern with the replacement string
|
|
19
|
+
modified_sentence = re.sub(r'\bis\b', 'was', sentence)
|
|
20
|
+
print(f"Sub: Modified sentence: '{modified_sentence}'")
|
|
21
|
+
# Example usage
|
|
22
|
+
sentence = "This is a test. This test is only a test."
|
|
23
|
+
demonstrate_regex_operations(sentence)
|
pybox/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Marker file for PEP 561. Pybox uses inline types.
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pybox-nlp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A production-ready Python library bundling 11 standalone NLP programs with source inspection and export capabilities.
|
|
5
|
+
Author: PyBox Maintainers
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/pybox-dev/pybox
|
|
8
|
+
Project-URL: Repository, https://github.com/pybox-dev/pybox
|
|
9
|
+
Project-URL: Issues, https://github.com/pybox-dev/pybox/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/pybox-dev/pybox/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: pybox,bundle,programs,source-code,exporter
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: build>=0.10.0; extra == "dev"
|
|
30
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# PyBox (`pybox`)
|
|
34
|
+
|
|
35
|
+
A production-ready Python library bundling 11 standalone Python programs into one installable package. Access each program via intuitive top-level attributes, inspect its original source code directly, and safely export it into standalone `.py` scripts.
|
|
36
|
+
|
|
37
|
+
[](https://pypi.org/project/pybox/)
|
|
38
|
+
[](https://pypi.org/project/pybox/)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Table of Contents
|
|
44
|
+
|
|
45
|
+
- [Overview](#overview)
|
|
46
|
+
- [Key Distinction: Inspection vs. Execution](#key-distinction-inspection-vs-execution)
|
|
47
|
+
- [Installation](#installation)
|
|
48
|
+
- [Quick Start](#quick-start)
|
|
49
|
+
- [Program Identifiers](#program-identifiers)
|
|
50
|
+
- [Features and Usage](#features-and-usage)
|
|
51
|
+
- [1. Viewing Source Code](#1-viewing-source-code)
|
|
52
|
+
- [2. Accessing Raw Source String](#2-accessing-raw-source-string)
|
|
53
|
+
- [3. Exporting to Standalone Files](#3-exporting-to-standalone-files)
|
|
54
|
+
- [4. Custom Export Destinations](#4-custom-export-destinations)
|
|
55
|
+
- [5. Overwrite Protection](#5-overwrite-protection)
|
|
56
|
+
- [6. Developer Representation](#6-developer-representation)
|
|
57
|
+
- [Individual Program Dependencies](#individual-program-dependencies)
|
|
58
|
+
- [Safety and Security](#safety-and-security)
|
|
59
|
+
- [Compatibility](#compatibility)
|
|
60
|
+
- [Development and Testing](#development-and-testing)
|
|
61
|
+
- [Publishing to PyPI](#publishing-to-pypi)
|
|
62
|
+
- [Contributing](#contributing)
|
|
63
|
+
- [License](#license)
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Overview
|
|
68
|
+
|
|
69
|
+
`pybox` bundles 11 standalone Python programs into a unified package. Instead of copying and pasting code snippets, developers can:
|
|
70
|
+
- Import `pybox` and inspect any bundled program's complete source code in Python interactive shells, logging, or scripts.
|
|
71
|
+
- Export bundled programs into standalone `.py` files on demand with atomic write safety and directory creation.
|
|
72
|
+
- Keep source code intact: all comments, imports, functions, classes, and logic are preserved identically.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Key Distinction: Inspection vs. Execution
|
|
77
|
+
|
|
78
|
+
> [!IMPORTANT]
|
|
79
|
+
> **`pybox` does NOT execute bundled programs when you import the library, inspect their source code, or export them to disk.**
|
|
80
|
+
|
|
81
|
+
`pybox` acts as a curated code distribution and extraction engine. When you access `pybox.one` or invoke `pybox.one.save()`, `pybox` reads the packaged source assets as UTF-8 text.
|
|
82
|
+
|
|
83
|
+
To **run** an exported program after saving it, execute it in Python directly using your terminal:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
python one.py
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Ensure any third-party dependencies required by that specific program are installed in your Python environment before running it.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Installation
|
|
94
|
+
|
|
95
|
+
### From PyPI
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
python -m pip install pybox
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### From Local Source (Editable Mode)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git clone https://github.com/pybox-dev/pybox.git
|
|
105
|
+
cd pybox
|
|
106
|
+
python -m pip install -e ".[dev]"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Quick Start
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import pybox
|
|
115
|
+
|
|
116
|
+
# 1. Print the complete source code of a program
|
|
117
|
+
print(pybox.one)
|
|
118
|
+
|
|
119
|
+
# 2. Get the raw source code string
|
|
120
|
+
code = pybox.two.code
|
|
121
|
+
print(f"Total lines: {len(code.splitlines())}")
|
|
122
|
+
|
|
123
|
+
# 3. Export to a standalone Python script
|
|
124
|
+
saved_path = pybox.one.save()
|
|
125
|
+
print(f"Exported to: {saved_path}")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Program Identifiers & Bundled Programs
|
|
131
|
+
|
|
132
|
+
The package exposes 11 curated NLP programs as attributes on the top-level `pybox` module:
|
|
133
|
+
|
|
134
|
+
| Attribute | Default Export | Description | Dependencies |
|
|
135
|
+
| :--- | :--- | :--- | :--- |
|
|
136
|
+
| `pybox.one` | `one.py` | **Word Frequency & Punctuation Stripper**: Counts word frequencies after stripping punctuation and normalizing case. | Standard Library (`string`) |
|
|
137
|
+
| `pybox.two` | `two.py` | **Regex Demonstrator**: Demonstrates pattern matching, searching, extracting, and substitution using regex. | Standard Library (`re`) |
|
|
138
|
+
| `pybox.three` | `three.py` | **Morphological Analyzer & Stopwords Filter**: Lemmatizes tokens and filters English stopwords. | `nltk` |
|
|
139
|
+
| `pybox.four` | `four.py` | **N-Gram Generator & Probability Calculator**: Generates N-grams and calculates sentence probabilities. | `nltk` |
|
|
140
|
+
| `pybox.five` | `five.py` | **Penn Treebank Component Analyzer**: Inspects and parses sentential constituents using Penn Treebank trees. | `nltk` |
|
|
141
|
+
| `pybox.six` | `six.py` | **Vectorization & Feature Extraction Pipeline**: Preprocesses text with stemming/lemmatization and extracts Count/TF-IDF vectors. | `nltk`, `scikit-learn` |
|
|
142
|
+
| `pybox.seven` | `seven.py` | **Context-Free Grammar (CFG) Generator**: Generates sentences adhering to formal phrase structure grammar rules. | `nltk` |
|
|
143
|
+
| `pybox.eight` | `eight.py` | **Word Sense Disambiguation (WSD)**: Resolves word senses in context using the Lesk algorithm and WordNet. | `nltk` |
|
|
144
|
+
| `pybox.nine` | `nine.py` | **Semantic Role & Feature Structure Identifier**: Maps syntactic tokens and POS tags to thematic roles and feature structures. | `nltk` |
|
|
145
|
+
| `pybox.ten` | `ten.py` | **First-Order Logic (FOL) Translator**: Translates quantified formal logic expressions into natural language sentences. | Standard Library (`re`) |
|
|
146
|
+
| `pybox.eleven` | `eleven.py` | **Selectional Restrictions & Anomaly Checker**: Evaluates semantic predicate-argument compatibility using feature structures. | `nltk` |
|
|
147
|
+
|
|
148
|
+
You can iterate over all programs programmatically:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
import pybox
|
|
152
|
+
|
|
153
|
+
for name, prog in pybox.PROGRAMS.items():
|
|
154
|
+
print(f"Loaded {prog.identifier}: {prog.filename}")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Features and Usage
|
|
160
|
+
|
|
161
|
+
### 1. Viewing Source Code
|
|
162
|
+
|
|
163
|
+
Converting any program object to `str` (or passing it to `print()`) renders its complete source code:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
import pybox
|
|
167
|
+
|
|
168
|
+
print(pybox.one)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 2. Accessing Raw Source String
|
|
172
|
+
|
|
173
|
+
Access the `.code` property on any program object to obtain the unmodified source code as a Python `str`:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
import pybox
|
|
177
|
+
|
|
178
|
+
source = pybox.one.code
|
|
179
|
+
assert isinstance(source, str)
|
|
180
|
+
print(f"Total lines: {len(source.splitlines())}")
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 3. Exporting to Standalone Files
|
|
184
|
+
|
|
185
|
+
Call `.save()` without arguments to export the program to the current working directory under its default filename (e.g., `one.py`):
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
import pybox
|
|
189
|
+
|
|
190
|
+
path = pybox.one.save()
|
|
191
|
+
print(path) # /path/to/cwd/one.py
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 4. Custom Export Destinations
|
|
195
|
+
|
|
196
|
+
Specify a custom file name or path. If intermediate parent directories do not exist, `pybox` creates them automatically:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
import pybox
|
|
200
|
+
|
|
201
|
+
# Export to a custom filename in the current directory
|
|
202
|
+
pybox.one.save("my_program.py")
|
|
203
|
+
|
|
204
|
+
# Export to a nested subdirectory (directories created automatically)
|
|
205
|
+
pybox.two.save("exports/tools/run_two.py")
|
|
206
|
+
|
|
207
|
+
# Export into an existing directory (uses default filename 'three.py')
|
|
208
|
+
pybox.three.save("exports/")
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### 5. Overwrite Protection
|
|
212
|
+
|
|
213
|
+
To protect existing scripts from accidental loss, `pybox` **refuses to overwrite existing files by default**:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
import pybox
|
|
217
|
+
|
|
218
|
+
# First export succeeds
|
|
219
|
+
pybox.one.save("script.py")
|
|
220
|
+
|
|
221
|
+
# Second export raises FileExistsError
|
|
222
|
+
try:
|
|
223
|
+
pybox.one.save("script.py")
|
|
224
|
+
except FileExistsError as e:
|
|
225
|
+
print("Caught expected protection error:", e)
|
|
226
|
+
|
|
227
|
+
# Explicitly authorize overwriting
|
|
228
|
+
pybox.one.save("script.py", overwrite=True)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Export writes use atomic temporary file replacement (`os.replace`) to ensure a partially written file is never left on disk in the event of an interruption.
|
|
232
|
+
|
|
233
|
+
### 6. Developer Representation
|
|
234
|
+
|
|
235
|
+
In interactive environments, `repr()` provides clear identification:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
import pybox
|
|
239
|
+
|
|
240
|
+
print(repr(pybox.one))
|
|
241
|
+
# Output: Program('one')
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Individual Program Dependencies
|
|
247
|
+
|
|
248
|
+
`pybox` itself has **zero runtime third-party dependencies**. It relies strictly on Python standard library modules (`pathlib`, `tempfile`, `os`, `importlib.resources`).
|
|
249
|
+
|
|
250
|
+
Exporting a program writes its standalone source code to disk. Before executing the exported scripts, install any required third-party libraries:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# To run all exported NLP programs:
|
|
254
|
+
pip install nltk scikit-learn
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
| Program | Standard Library Modules | External Dependencies | NLTK Corpora Required (Auto-downloaded on run) |
|
|
258
|
+
| :--- | :--- | :--- | :--- |
|
|
259
|
+
| `one.py` | `string` | None | None |
|
|
260
|
+
| `two.py` | `re` | None | None |
|
|
261
|
+
| `three.py` | Standard library | `nltk` | `punkt`, `wordnet`, `stopwords` |
|
|
262
|
+
| `four.py` | `collections`, `math` | `nltk` | `punkt` |
|
|
263
|
+
| `five.py` | Standard library | `nltk` | `treebank`, `punkt` |
|
|
264
|
+
| `six.py` | Standard library | `nltk`, `scikit-learn` | `punkt`, `stopwords`, `wordnet` |
|
|
265
|
+
| `seven.py` | Standard library | `nltk` | None |
|
|
266
|
+
| `eight.py` | Standard library | `nltk` | `wordnet`, `punkt`, `omw-1.4` |
|
|
267
|
+
| `nine.py` | Standard library | `nltk` | `punkt`, `averaged_perceptron_tagger`, `wordnet` |
|
|
268
|
+
| `ten.py` | `re` | None | None |
|
|
269
|
+
| `eleven.py` | Standard library | `nltk` | None |
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Safety and Security
|
|
274
|
+
|
|
275
|
+
- **No Execution on Import**: Bundled code is never imported, evaluated, or executed by `pybox`.
|
|
276
|
+
- **Atomic Writes**: Export files are written to a temporary file before being atomically moved into destination path.
|
|
277
|
+
- **Directory Traversal & Overwrite Guards**: Prevents accidental directory replacement and unintended data destruction.
|
|
278
|
+
- **Zero Secrets**: Bundled programs must never contain API tokens, passwords, or personal credentials.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Compatibility
|
|
283
|
+
|
|
284
|
+
- **Python**: `>= 3.8` (fully tested across Python 3.8, 3.9, 3.10, 3.11, 3.12, and 3.13)
|
|
285
|
+
- **Platforms**: Windows, macOS, and Linux
|
|
286
|
+
- **Packaging Standard**: PEP 517 / PEP 518 / PEP 621 compliant build
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Development and Testing
|
|
291
|
+
|
|
292
|
+
### Setup Environment
|
|
293
|
+
|
|
294
|
+
```powershell
|
|
295
|
+
# Windows PowerShell
|
|
296
|
+
python -m venv .venv
|
|
297
|
+
.\.venv\Scripts\Activate.ps1
|
|
298
|
+
python -m pip install --upgrade pip
|
|
299
|
+
python -m pip install -e ".[dev]"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Linux / macOS
|
|
304
|
+
python3 -m venv .venv
|
|
305
|
+
source .venv/bin/activate
|
|
306
|
+
python -m pip install --upgrade pip
|
|
307
|
+
python -m pip install -e ".[dev]"
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Run Tests
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
pytest -v
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Build Distribution Artifacts
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
python -m build
|
|
320
|
+
python -m twine check dist/*
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Publishing to PyPI
|
|
326
|
+
|
|
327
|
+
> [!NOTE]
|
|
328
|
+
> PyPI distribution names are globally unique. Before publishing, verify name availability on [PyPI](https://pypi.org/). If `pybox` is already registered, set an alternative name (such as `pybox-lib` or `pybox-bundle`) in `pyproject.toml`.
|
|
329
|
+
|
|
330
|
+
### Option A: PyPI Trusted Publishing (Recommended)
|
|
331
|
+
|
|
332
|
+
1. Connect your GitHub repository to PyPI at [PyPI Manage Account > Publishing](https://pypi.org/manage/account/publishing/).
|
|
333
|
+
2. Push a release tag or trigger the included `.github/workflows/publish.yml` workflow.
|
|
334
|
+
3. GitHub Actions authenticates via short-lived OIDC tokens without requiring static API keys.
|
|
335
|
+
|
|
336
|
+
### Option B: Manual Publishing via Twine
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
python -m twine upload --repository testpypi dist/* # Test on TestPyPI first
|
|
340
|
+
python -m twine upload dist/* # Publish to production PyPI
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Contributing
|
|
346
|
+
|
|
347
|
+
1. Fork the repository.
|
|
348
|
+
2. Create a feature branch (`git checkout -b feature/my-feature`).
|
|
349
|
+
3. Commit your changes (`git commit -m "Add feature"`).
|
|
350
|
+
4. Run the test suite (`pytest -v`).
|
|
351
|
+
5. Open a Pull Request.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## License
|
|
356
|
+
|
|
357
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
pybox/__init__.py,sha256=pa2TYgUk_K5hjAL5Jp-wEHPU4buT7JMsHkgylL3kNPk,1073
|
|
2
|
+
pybox/core.py,sha256=spTNJVfcTWty1dPfbKtAj6fYc9fBhXZk4hWweRCH2Xg,5785
|
|
3
|
+
pybox/py.typed,sha256=I4fsYtPBETPV1VDKpOCZp2k9qCuc2zWbrnA9pg1evyk,52
|
|
4
|
+
pybox/programs/__init__.py,sha256=vhZnSCIgwO5LMmsJ-5zb0mT8f--kfZY6w9gbkureCcU,176
|
|
5
|
+
pybox/programs/eight.py,sha256=22LuJzc5twkmfCLXfaeWGfC0yCkWw6oAOopHJ2D2A8c,941
|
|
6
|
+
pybox/programs/eleven.py,sha256=_89admk5a6myq_mwtx5Yid4qVd-3UaShxvHRNFAlx5k,1654
|
|
7
|
+
pybox/programs/five.py,sha256=quVgZ1finrsD4hVXkAhjWSeC83I-clRelXrPT28WUes,1041
|
|
8
|
+
pybox/programs/four.py,sha256=n_oEPHDwC2WNuJoWoK8x9D0nckX0tBnaX9yyA_jW4VQ,1389
|
|
9
|
+
pybox/programs/nine.py,sha256=fBjfbd3XAPuVBArZMJIEQeS41lvy7Q6F58r099wrOF0,1545
|
|
10
|
+
pybox/programs/one.py,sha256=hxJk0MSob9uGkuKv51wgq5RrX0lgr0yLx8Rm37XYcZE,771
|
|
11
|
+
pybox/programs/seven.py,sha256=DU78Z1aFxugQSuFTuL8pJcSVPkMjBJhS2rpx6DTFFa4,1150
|
|
12
|
+
pybox/programs/six.py,sha256=Mfp7fxA7LW6R6guk60-0JbIvwgFqOAnhnK1mFp2KziQ,2316
|
|
13
|
+
pybox/programs/ten.py,sha256=hDSlEB--JQGJopOo3hC-7JVj0xFmDIXIxUP1sBXlyp0,1043
|
|
14
|
+
pybox/programs/three.py,sha256=l6FSS0OJdlvPnsdUS8iUxq03KCshaABDS_XePUO-ayA,1031
|
|
15
|
+
pybox/programs/two.py,sha256=G2hsReL4qiAWD2F7oNssd2vCMjVaLIaSlY92lnRmuPw,1028
|
|
16
|
+
pybox_nlp-0.1.0.dist-info/licenses/LICENSE,sha256=HyKa_6xXkhmQKyj4ymCYD64vEu7jzRafTT-eIPlPOy0,1087
|
|
17
|
+
pybox_nlp-0.1.0.dist-info/METADATA,sha256=Ohr0ftWe4aVEbvnb31ppAgNfRjQLJua0YlbPAtbe4Sc,12518
|
|
18
|
+
pybox_nlp-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
19
|
+
pybox_nlp-0.1.0.dist-info/top_level.txt,sha256=jyiglQVZaayV6LzbWjl4RERgL92aJ9-L1WRp2OUTJbg,6
|
|
20
|
+
pybox_nlp-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PyBox Authors and Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pybox
|