Lexitron 2.0.4__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,17 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: Lexitron
3
- Version: 2.0.4
3
+ Version: 2.1.2
4
4
  Summary: A command-line regex search engine for the English language
5
- Home-page: http://github.com/rothos/lexitron
6
5
  Author: Rothos
7
6
  License: GNU GPL Version 2
7
+ Project-URL: Homepage, http://github.com/rothos/lexitron
8
8
  Keywords: english,dictionary,crossword,spelling,words,lexicon,search
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Requires-Python: >=3.7
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
+ Requires-Dist: importlib-metadata; python_version < "3.8"
14
+ Requires-Dist: importlib-resources; python_version < "3.8"
13
15
 
14
16
  Lexitron
15
17
  ==============================================================================
@@ -57,11 +59,12 @@ where `expression` is a regular expression and `[options]` are as follows.
57
59
 
58
60
  option | function
59
61
  --------|-------------------------------------------------
60
- `-d` | Append start and end delimiters `^...$` to search query
62
+ `-h` | Print help and exit
63
+ `-i` | Print info header along with search results
61
64
  `-n` | Print only the number of matches
62
- `-u` | Search only for lowercase/common/uncapitalized words
63
- `-U` | Search only for uppercase/proper/capitalized words
64
- `-v` | Show version and exit
65
+ `-u` | Include uppercase/proper words (like "France") in addition to lowercase/common words
66
+ `-U` | Search only for uppercase/proper words
67
+ `-v` | Print version and exit
65
68
  `-x` | Print unformatted output, one word per line
66
69
 
67
70
  Type `$ lx -h` for full help text.
@@ -87,37 +90,30 @@ Examples
87
90
  ------------------------------------------------------------------------------
88
91
 
89
92
  ### Example 1
90
- A list of English words ending with "icide".
93
+ A list of lowercase English words ending with "icide".
91
94
  ```
92
95
  $ lx icide$
93
- ---------------------------------------------------------------------------
94
- 53 results for /.*icide/
95
- 0 proper ~ 53 common
96
- ---------------------------------------------------------------------------
97
-
98
- aborticide foeticide matricide pesticide stillicide
99
- acaricide fratricide medicide prolicide suicide
100
- agricide fungicide menticide pulicide tyrannicide
101
- algicide germicide miticide raticide uxoricide
102
- aphicide giganticide molluscicide regicide vaticide
103
- aphidicide herbicide nematicide rodenticide verbicide
104
- bacillicide homicide ovicide scabicide vermicide
105
- bactericide infanticide parasiticide silicide viricide
106
- deicide insecticide parasuicide sororicide vulpicide
107
- feticide larvicide parricide spermicide
108
- filicide liberticide patricide sporicide
96
+ aborticide germicide ovicide spermicide
97
+ acaricide giganticide parasiticide sporicide
98
+ agricide herbicide parasuicide stillicide
99
+ algicide homicide parricide suicide
100
+ aphicide infanticide patricide tyrannicide
101
+ aphidicide insecticide pesticide uxoricide
102
+ bacillicide larvicide prolicide vaticide
103
+ bactericide liberticide pulicide verbicide
104
+ deicide matricide raticide vermicide
105
+ feticide medicide regicide viricide
106
+ filicide menticide rodenticide vulpicide
107
+ foeticide miticide scabicide
108
+ fratricide molluscicide silicide
109
+ fungicide nematicide sororicide
109
110
  ```
110
111
 
111
112
  ### Example 2
112
- A list of English words that contain the substring "rdb".
113
+ A list of lowercase English words that contain the substring "rdb", printed with info header.
113
114
  ```
114
- $ lx rdb
115
- ---------------------------------------------------------------------------
116
- 21 results for /rdb/
117
- 1 proper ~ 20 common
118
- ---------------------------------------------------------------------------
119
-
120
- Standardbred
115
+ $ lx -i rdb
116
+ 20 matches for /rdb/
121
117
 
122
118
  birdbath herdbook
123
119
  birdbrain herdboy
@@ -134,7 +130,7 @@ hardbound yardbird
134
130
  ### Example 3
135
131
  The number of lowercase English words that end in "tion".
136
132
  ```
137
- $ lx -nxu ".*tion"
133
+ $ lx -n ".*tion"
138
134
  3837
139
135
  ```
140
136
  (This number should be taken with a grain of salt, since no dictionary
@@ -145,13 +141,10 @@ and which technical or niche jargons are included; etc etc.)
145
141
  ### Example 4
146
142
  A list of English words with the same double letter appearing twice, except
147
143
  for those whose double letter is a vowel or the letter `s` (to ignore
148
- words of the form `*lessness`).
144
+ words of the form `*lessness`), printed with info header.
149
145
  ```
150
- $ lx "([^aeious])\1.*\1\1"
151
- ---------------------------------------------------------------------------
152
- 45 results for /([^aeious])\1.*\1\1/
153
- 9 proper ~ 36 common
154
- ---------------------------------------------------------------------------
146
+ $ lx -iu "([^aeious])\1.*\1\1"
147
+ 45 matches for /([^aeious])\1.*\1\1/ (9 proper, 36 common)
155
148
 
156
149
  Allhallowmas
157
150
  Allhallows
@@ -163,26 +156,29 @@ Gallirallus
163
156
  Hunnemannia
164
157
  Llullaillaco
165
158
 
166
- acciaccatura hillbilly pellmell shillyshally
167
- bellpull huggermugger pizzazz skillfully
168
- chiffchaff hullaballoo pralltriller snippersnapper
169
- dillydallier jellyroll razzamatazz villanelle
170
- dillydally kinnikinnic razzmatazz volleyball
171
- dullsville kinnikinnick riffraff volleyballer
172
- flibbertigibbet millefeuille rollcollar whippersnapper
173
- granddaddy niffnaff rollerball willfully
174
- hallalling parallelling scuttlebutt yellowbelly
159
+ acciaccatura jellyroll rollcollar
160
+ bellpull kinnikinnic rollerball
161
+ chiffchaff kinnikinnick scuttlebutt
162
+ dillydallier millefeuille shillyshally
163
+ dillydally niffnaff skillfully
164
+ dullsville parallelling snippersnapper
165
+ flibbertigibbet pellmell villanelle
166
+ granddaddy pizzazz volleyball
167
+ hallalling pralltriller volleyballer
168
+ hillbilly razzamatazz whippersnapper
169
+ huggermugger razzmatazz willfully
170
+ hullaballoo riffraff yellowbelly
175
171
  ```
176
172
 
177
173
  ### Example 5
178
- Compare the number of lowercase/uncapitalized words that end in "woman"
174
+ Compare the number of lowercase English words that end in "woman"
179
175
  with the number that end in "man".
180
176
  ```
181
- $ lx -nxu ".*woman"
177
+ $ lx -n ".*woman"
182
178
  107
183
179
  ```
184
180
  ```
185
- $ lx -nxu ".*(?<\!wo)man"
181
+ $ lx -n ".*(?<\!wo)man"
186
182
  1145
187
183
  ```
188
184
 
@@ -202,4 +198,4 @@ Lexitron is licensed under GNU GPL Version 2.
202
198
  Contact
203
199
  ------------------------------------------------------------------------------
204
200
  Questions, bug reports, and feature requests can be filed on the [Github
205
- issues tracker](//github.com/hrothgar/lexitron/issues).
201
+ issues tracker](//github.com/rothos/lexitron/issues).
@@ -1,7 +1,6 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
- setup.cfg
5
4
  ./lexitron/__init__.py
6
5
  ./lexitron/agid-common.txt
7
6
  ./lexitron/agid-proper.txt
@@ -12,4 +11,8 @@ Lexitron.egg-info/dependency_links.txt
12
11
  Lexitron.egg-info/entry_points.txt
13
12
  Lexitron.egg-info/not-zip-safe
14
13
  Lexitron.egg-info/requires.txt
15
- Lexitron.egg-info/top_level.txt
14
+ Lexitron.egg-info/top_level.txt
15
+ lexitron/__init__.py
16
+ lexitron/agid-common.txt
17
+ lexitron/agid-proper.txt
18
+ lexitron/lexitron.py
@@ -1,15 +1,17 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: Lexitron
3
- Version: 2.0.4
3
+ Version: 2.1.2
4
4
  Summary: A command-line regex search engine for the English language
5
- Home-page: http://github.com/rothos/lexitron
6
5
  Author: Rothos
7
6
  License: GNU GPL Version 2
7
+ Project-URL: Homepage, http://github.com/rothos/lexitron
8
8
  Keywords: english,dictionary,crossword,spelling,words,lexicon,search
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Requires-Python: >=3.7
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
+ Requires-Dist: importlib-metadata; python_version < "3.8"
14
+ Requires-Dist: importlib-resources; python_version < "3.8"
13
15
 
14
16
  Lexitron
15
17
  ==============================================================================
@@ -57,11 +59,12 @@ where `expression` is a regular expression and `[options]` are as follows.
57
59
 
58
60
  option | function
59
61
  --------|-------------------------------------------------
60
- `-d` | Append start and end delimiters `^...$` to search query
62
+ `-h` | Print help and exit
63
+ `-i` | Print info header along with search results
61
64
  `-n` | Print only the number of matches
62
- `-u` | Search only for lowercase/common/uncapitalized words
63
- `-U` | Search only for uppercase/proper/capitalized words
64
- `-v` | Show version and exit
65
+ `-u` | Include uppercase/proper words (like "France") in addition to lowercase/common words
66
+ `-U` | Search only for uppercase/proper words
67
+ `-v` | Print version and exit
65
68
  `-x` | Print unformatted output, one word per line
66
69
 
67
70
  Type `$ lx -h` for full help text.
@@ -87,37 +90,30 @@ Examples
87
90
  ------------------------------------------------------------------------------
88
91
 
89
92
  ### Example 1
90
- A list of English words ending with "icide".
93
+ A list of lowercase English words ending with "icide".
91
94
  ```
92
95
  $ lx icide$
93
- ---------------------------------------------------------------------------
94
- 53 results for /.*icide/
95
- 0 proper ~ 53 common
96
- ---------------------------------------------------------------------------
97
-
98
- aborticide foeticide matricide pesticide stillicide
99
- acaricide fratricide medicide prolicide suicide
100
- agricide fungicide menticide pulicide tyrannicide
101
- algicide germicide miticide raticide uxoricide
102
- aphicide giganticide molluscicide regicide vaticide
103
- aphidicide herbicide nematicide rodenticide verbicide
104
- bacillicide homicide ovicide scabicide vermicide
105
- bactericide infanticide parasiticide silicide viricide
106
- deicide insecticide parasuicide sororicide vulpicide
107
- feticide larvicide parricide spermicide
108
- filicide liberticide patricide sporicide
96
+ aborticide germicide ovicide spermicide
97
+ acaricide giganticide parasiticide sporicide
98
+ agricide herbicide parasuicide stillicide
99
+ algicide homicide parricide suicide
100
+ aphicide infanticide patricide tyrannicide
101
+ aphidicide insecticide pesticide uxoricide
102
+ bacillicide larvicide prolicide vaticide
103
+ bactericide liberticide pulicide verbicide
104
+ deicide matricide raticide vermicide
105
+ feticide medicide regicide viricide
106
+ filicide menticide rodenticide vulpicide
107
+ foeticide miticide scabicide
108
+ fratricide molluscicide silicide
109
+ fungicide nematicide sororicide
109
110
  ```
110
111
 
111
112
  ### Example 2
112
- A list of English words that contain the substring "rdb".
113
+ A list of lowercase English words that contain the substring "rdb", printed with info header.
113
114
  ```
114
- $ lx rdb
115
- ---------------------------------------------------------------------------
116
- 21 results for /rdb/
117
- 1 proper ~ 20 common
118
- ---------------------------------------------------------------------------
119
-
120
- Standardbred
115
+ $ lx -i rdb
116
+ 20 matches for /rdb/
121
117
 
122
118
  birdbath herdbook
123
119
  birdbrain herdboy
@@ -134,7 +130,7 @@ hardbound yardbird
134
130
  ### Example 3
135
131
  The number of lowercase English words that end in "tion".
136
132
  ```
137
- $ lx -nxu ".*tion"
133
+ $ lx -n ".*tion"
138
134
  3837
139
135
  ```
140
136
  (This number should be taken with a grain of salt, since no dictionary
@@ -145,13 +141,10 @@ and which technical or niche jargons are included; etc etc.)
145
141
  ### Example 4
146
142
  A list of English words with the same double letter appearing twice, except
147
143
  for those whose double letter is a vowel or the letter `s` (to ignore
148
- words of the form `*lessness`).
144
+ words of the form `*lessness`), printed with info header.
149
145
  ```
150
- $ lx "([^aeious])\1.*\1\1"
151
- ---------------------------------------------------------------------------
152
- 45 results for /([^aeious])\1.*\1\1/
153
- 9 proper ~ 36 common
154
- ---------------------------------------------------------------------------
146
+ $ lx -iu "([^aeious])\1.*\1\1"
147
+ 45 matches for /([^aeious])\1.*\1\1/ (9 proper, 36 common)
155
148
 
156
149
  Allhallowmas
157
150
  Allhallows
@@ -163,26 +156,29 @@ Gallirallus
163
156
  Hunnemannia
164
157
  Llullaillaco
165
158
 
166
- acciaccatura hillbilly pellmell shillyshally
167
- bellpull huggermugger pizzazz skillfully
168
- chiffchaff hullaballoo pralltriller snippersnapper
169
- dillydallier jellyroll razzamatazz villanelle
170
- dillydally kinnikinnic razzmatazz volleyball
171
- dullsville kinnikinnick riffraff volleyballer
172
- flibbertigibbet millefeuille rollcollar whippersnapper
173
- granddaddy niffnaff rollerball willfully
174
- hallalling parallelling scuttlebutt yellowbelly
159
+ acciaccatura jellyroll rollcollar
160
+ bellpull kinnikinnic rollerball
161
+ chiffchaff kinnikinnick scuttlebutt
162
+ dillydallier millefeuille shillyshally
163
+ dillydally niffnaff skillfully
164
+ dullsville parallelling snippersnapper
165
+ flibbertigibbet pellmell villanelle
166
+ granddaddy pizzazz volleyball
167
+ hallalling pralltriller volleyballer
168
+ hillbilly razzamatazz whippersnapper
169
+ huggermugger razzmatazz willfully
170
+ hullaballoo riffraff yellowbelly
175
171
  ```
176
172
 
177
173
  ### Example 5
178
- Compare the number of lowercase/uncapitalized words that end in "woman"
174
+ Compare the number of lowercase English words that end in "woman"
179
175
  with the number that end in "man".
180
176
  ```
181
- $ lx -nxu ".*woman"
177
+ $ lx -n ".*woman"
182
178
  107
183
179
  ```
184
180
  ```
185
- $ lx -nxu ".*(?<\!wo)man"
181
+ $ lx -n ".*(?<\!wo)man"
186
182
  1145
187
183
  ```
188
184
 
@@ -202,4 +198,4 @@ Lexitron is licensed under GNU GPL Version 2.
202
198
  Contact
203
199
  ------------------------------------------------------------------------------
204
200
  Questions, bug reports, and feature requests can be filed on the [Github
205
- issues tracker](//github.com/hrothgar/lexitron/issues).
201
+ issues tracker](//github.com/rothos/lexitron/issues).
@@ -44,11 +44,12 @@ where `expression` is a regular expression and `[options]` are as follows.
44
44
 
45
45
  option | function
46
46
  --------|-------------------------------------------------
47
- `-d` | Append start and end delimiters `^...$` to search query
47
+ `-h` | Print help and exit
48
+ `-i` | Print info header along with search results
48
49
  `-n` | Print only the number of matches
49
- `-u` | Search only for lowercase/common/uncapitalized words
50
- `-U` | Search only for uppercase/proper/capitalized words
51
- `-v` | Show version and exit
50
+ `-u` | Include uppercase/proper words (like "France") in addition to lowercase/common words
51
+ `-U` | Search only for uppercase/proper words
52
+ `-v` | Print version and exit
52
53
  `-x` | Print unformatted output, one word per line
53
54
 
54
55
  Type `$ lx -h` for full help text.
@@ -74,37 +75,30 @@ Examples
74
75
  ------------------------------------------------------------------------------
75
76
 
76
77
  ### Example 1
77
- A list of English words ending with "icide".
78
+ A list of lowercase English words ending with "icide".
78
79
  ```
79
80
  $ lx icide$
80
- ---------------------------------------------------------------------------
81
- 53 results for /.*icide/
82
- 0 proper ~ 53 common
83
- ---------------------------------------------------------------------------
84
-
85
- aborticide foeticide matricide pesticide stillicide
86
- acaricide fratricide medicide prolicide suicide
87
- agricide fungicide menticide pulicide tyrannicide
88
- algicide germicide miticide raticide uxoricide
89
- aphicide giganticide molluscicide regicide vaticide
90
- aphidicide herbicide nematicide rodenticide verbicide
91
- bacillicide homicide ovicide scabicide vermicide
92
- bactericide infanticide parasiticide silicide viricide
93
- deicide insecticide parasuicide sororicide vulpicide
94
- feticide larvicide parricide spermicide
95
- filicide liberticide patricide sporicide
81
+ aborticide germicide ovicide spermicide
82
+ acaricide giganticide parasiticide sporicide
83
+ agricide herbicide parasuicide stillicide
84
+ algicide homicide parricide suicide
85
+ aphicide infanticide patricide tyrannicide
86
+ aphidicide insecticide pesticide uxoricide
87
+ bacillicide larvicide prolicide vaticide
88
+ bactericide liberticide pulicide verbicide
89
+ deicide matricide raticide vermicide
90
+ feticide medicide regicide viricide
91
+ filicide menticide rodenticide vulpicide
92
+ foeticide miticide scabicide
93
+ fratricide molluscicide silicide
94
+ fungicide nematicide sororicide
96
95
  ```
97
96
 
98
97
  ### Example 2
99
- A list of English words that contain the substring "rdb".
98
+ A list of lowercase English words that contain the substring "rdb", printed with info header.
100
99
  ```
101
- $ lx rdb
102
- ---------------------------------------------------------------------------
103
- 21 results for /rdb/
104
- 1 proper ~ 20 common
105
- ---------------------------------------------------------------------------
106
-
107
- Standardbred
100
+ $ lx -i rdb
101
+ 20 matches for /rdb/
108
102
 
109
103
  birdbath herdbook
110
104
  birdbrain herdboy
@@ -121,7 +115,7 @@ hardbound yardbird
121
115
  ### Example 3
122
116
  The number of lowercase English words that end in "tion".
123
117
  ```
124
- $ lx -nxu ".*tion"
118
+ $ lx -n ".*tion"
125
119
  3837
126
120
  ```
127
121
  (This number should be taken with a grain of salt, since no dictionary
@@ -132,13 +126,10 @@ and which technical or niche jargons are included; etc etc.)
132
126
  ### Example 4
133
127
  A list of English words with the same double letter appearing twice, except
134
128
  for those whose double letter is a vowel or the letter `s` (to ignore
135
- words of the form `*lessness`).
129
+ words of the form `*lessness`), printed with info header.
136
130
  ```
137
- $ lx "([^aeious])\1.*\1\1"
138
- ---------------------------------------------------------------------------
139
- 45 results for /([^aeious])\1.*\1\1/
140
- 9 proper ~ 36 common
141
- ---------------------------------------------------------------------------
131
+ $ lx -iu "([^aeious])\1.*\1\1"
132
+ 45 matches for /([^aeious])\1.*\1\1/ (9 proper, 36 common)
142
133
 
143
134
  Allhallowmas
144
135
  Allhallows
@@ -150,26 +141,29 @@ Gallirallus
150
141
  Hunnemannia
151
142
  Llullaillaco
152
143
 
153
- acciaccatura hillbilly pellmell shillyshally
154
- bellpull huggermugger pizzazz skillfully
155
- chiffchaff hullaballoo pralltriller snippersnapper
156
- dillydallier jellyroll razzamatazz villanelle
157
- dillydally kinnikinnic razzmatazz volleyball
158
- dullsville kinnikinnick riffraff volleyballer
159
- flibbertigibbet millefeuille rollcollar whippersnapper
160
- granddaddy niffnaff rollerball willfully
161
- hallalling parallelling scuttlebutt yellowbelly
144
+ acciaccatura jellyroll rollcollar
145
+ bellpull kinnikinnic rollerball
146
+ chiffchaff kinnikinnick scuttlebutt
147
+ dillydallier millefeuille shillyshally
148
+ dillydally niffnaff skillfully
149
+ dullsville parallelling snippersnapper
150
+ flibbertigibbet pellmell villanelle
151
+ granddaddy pizzazz volleyball
152
+ hallalling pralltriller volleyballer
153
+ hillbilly razzamatazz whippersnapper
154
+ huggermugger razzmatazz willfully
155
+ hullaballoo riffraff yellowbelly
162
156
  ```
163
157
 
164
158
  ### Example 5
165
- Compare the number of lowercase/uncapitalized words that end in "woman"
159
+ Compare the number of lowercase English words that end in "woman"
166
160
  with the number that end in "man".
167
161
  ```
168
- $ lx -nxu ".*woman"
162
+ $ lx -n ".*woman"
169
163
  107
170
164
  ```
171
165
  ```
172
- $ lx -nxu ".*(?<\!wo)man"
166
+ $ lx -n ".*(?<\!wo)man"
173
167
  1145
174
168
  ```
175
169
 
@@ -189,4 +183,4 @@ Lexitron is licensed under GNU GPL Version 2.
189
183
  Contact
190
184
  ------------------------------------------------------------------------------
191
185
  Questions, bug reports, and feature requests can be filed on the [Github
192
- issues tracker](//github.com/hrothgar/lexitron/issues).
186
+ issues tracker](//github.com/rothos/lexitron/issues).
@@ -0,0 +1,282 @@
1
+ import sys, os, re, argparse
2
+ from importlib import resources, metadata
3
+ from math import ceil, floor
4
+ from typing import Dict, List, Tuple
5
+
6
+ class Lexitron:
7
+ """Main class for the Lexitron command line tool."""
8
+
9
+ # Configuration constants
10
+ DEFAULT_ENCODING = 'utf-8'
11
+ WORDLIST_PATHS = {
12
+ 'common': 'agid-common.txt',
13
+ 'proper': 'agid-proper.txt'
14
+ }
15
+ FORMATTING = {
16
+ 'gutter_width': 4,
17
+ 'terminal_margin': 1,
18
+ 'show_rules': False
19
+ }
20
+
21
+ def __init__(self):
22
+ """Initialize Lexitron with wordlist files and argument parser configuration."""
23
+ # The wordlist files
24
+ self.wordlist_common_file = resources.files('lexitron').joinpath(self.WORDLIST_PATHS['common'])
25
+ self.wordlist_proper_file = resources.files('lexitron').joinpath(self.WORDLIST_PATHS['proper'])
26
+
27
+ # Here is the argument parser
28
+ description = "Lexitron, a regex search engine for the English " +\
29
+ "language. By default, Lexitron searches only for common " +\
30
+ "(lowercase) words."
31
+ epilog = "Currently, Lexitron only allows case-insensitive " +\
32
+ "searching.\n\nSee <https://github.com/rothos/lexitron> for " +\
33
+ "further documentation and examples."
34
+ self.parser = argparse.ArgumentParser(prog='lx',
35
+ description=description, epilog=epilog)
36
+
37
+ # Add the positional argument
38
+ self.parser.add_argument('expression', type=str,
39
+ help='the regular expression to search')
40
+
41
+ # Add the optional arguments
42
+ self.parser.add_argument('-n', '--number-only',
43
+ dest='number', action='store_true',
44
+ help='print only the total number of matches')
45
+ self.parser.add_argument('-i', '--info',
46
+ dest='show_info', action='store_true',
47
+ help='print info header along with search results')
48
+ self.parser.add_argument('-u', '--uppercase',
49
+ dest='include_proper', action='store_true',
50
+ help='include uppercase/proper words (like "France") in addition to lowercase/common words')
51
+ self.parser.add_argument('-U', '--only-uppercase',
52
+ dest='only_proper', action='store_true',
53
+ help='search only for uppercase/proper words')
54
+ self.parser.add_argument('-v', '--version',
55
+ action='version', version="Lexitron "+metadata.version('lexitron'),
56
+ help='print version and exit')
57
+ self.parser.add_argument('-x', '--plain',
58
+ dest='unformatted', action='store_true',
59
+ help='print unformatted output, one word per line')
60
+
61
+ def print_help(self):
62
+ """Display the help message for the command line interface."""
63
+ self.parser.print_help()
64
+
65
+ def parse_args(self, argv: List[str]) -> argparse.Namespace:
66
+ """Parse command line arguments.
67
+
68
+ Args:
69
+ argv: List of command line arguments
70
+
71
+ Returns:
72
+ Parsed argument namespace
73
+ """
74
+ # Parse the incoming arguments
75
+ args = self.parser.parse_args(argv)
76
+ return args
77
+
78
+ def search(self, args: argparse.Namespace) -> Dict[str, List[str]]:
79
+ """Search wordlists for matches to the given regular expression.
80
+
81
+ Args:
82
+ args: Parsed command line arguments containing search parameters
83
+
84
+ Returns:
85
+ Dictionary with 'common' and 'proper' lists of matching words
86
+ """
87
+ # Get the expression ready.
88
+ expr = self.sanitize(args.expression)
89
+
90
+ # Currently, lexitron search is case-insensitive.
91
+ expr = re.compile(expr, flags=re.IGNORECASE)
92
+ matches = {'common': [], 'proper': []}
93
+
94
+ # Now open each wordlist and start searching.
95
+ # Always search common words unless only_proper is specified
96
+ if not args.only_proper:
97
+ for word in self._iter_words(self.wordlist_common_file):
98
+ if expr.search(word):
99
+ matches['common'].append(word)
100
+
101
+ # Only search proper words if include_proper or only_proper is specified
102
+ if args.include_proper or args.only_proper:
103
+ for word in self._iter_words(self.wordlist_proper_file):
104
+ if expr.search(word):
105
+ matches['proper'].append(word)
106
+
107
+ matches['common'] = sorted(list(set(matches['common'])))
108
+ matches['proper'] = sorted(list(set(matches['proper'])))
109
+
110
+ return matches
111
+
112
+ def sanitize(self, expr: str) -> str:
113
+ """Remove wrapping quotation marks from the input expression.
114
+
115
+ Args:
116
+ expr: Regular expression string
117
+
118
+ Returns:
119
+ Sanitized expression string
120
+ """
121
+ # Remove any wrapping quotation marks from the input.
122
+ if expr[0] in ('"',"'"): expr = expr[1:]
123
+ if expr[-1] in ('"',"'"): expr = expr[:-1]
124
+ return expr
125
+
126
+ def _iter_words(self, wordlist_file) -> str:
127
+ """Iterate through words in a wordlist file.
128
+
129
+ Args:
130
+ wordlist_file: A Path object pointing to the wordlist file
131
+
132
+ Yields:
133
+ str: Each word from the file, one at a time
134
+ """
135
+ with wordlist_file.open('rb') as f:
136
+ for line in f:
137
+ line = line.strip()
138
+ if line and not line.startswith(b'#'): # Skip empty lines and comments
139
+ try:
140
+ yield self.parse_line(line)
141
+ except LexitronParseError:
142
+ continue # Skip lines that can't be parsed
143
+
144
+ def parse_line(self, line: bytes) -> str:
145
+ """Parse a line from the wordlist file.
146
+
147
+ Args:
148
+ line: A line from the wordlist file
149
+
150
+ Returns:
151
+ The word from the line
152
+ """
153
+ # This only returns the word right now, but since the agid.txt list
154
+ # contains other information (part of speech, derivative words, etc),
155
+ # the search may become more robust in the future.
156
+ try:
157
+ return line.split()[0].decode()
158
+ except (IndexError, UnicodeDecodeError) as e:
159
+ raise LexitronParseError(f"Failed to parse line: {e}")
160
+
161
+ def print_results(self, matches: Dict[str, List[str]], args: argparse.Namespace):
162
+ """Print search results according to specified format.
163
+
164
+ Args:
165
+ matches: Dictionary containing lists of matching common and proper words
166
+ args: Parsed arguments controlling output format
167
+ """
168
+ commons = matches['common']
169
+ propers = matches['proper']
170
+
171
+ if args.unformatted:
172
+ if args.number:
173
+ # Unformatted number only
174
+ print(str(len(propers + commons)))
175
+ else:
176
+ # Unformatted matches
177
+ print('\n'.join(propers + commons))
178
+ else:
179
+ width, height = os.get_terminal_size()
180
+ width = width - 1 # Wiggle room
181
+
182
+ if args.number:
183
+ # Print only the total number
184
+ print(len(propers + commons))
185
+ else:
186
+ # Print header if requested
187
+ if args.show_info:
188
+ print(self.header(commons, propers, args, width))
189
+ if propers or commons:
190
+ print()
191
+
192
+ # Print matches
193
+ if propers:
194
+ print(self.formatted(propers, width, height))
195
+ if commons and propers:
196
+ print()
197
+ if commons:
198
+ print(self.formatted(commons, width, height))
199
+
200
+ def header(self, commons: List[str], propers: List[str], args: argparse.Namespace, width: int) -> str:
201
+ """Generate header string showing search results summary.
202
+
203
+ Args:
204
+ commons: List of matching common words
205
+ propers: List of matching proper words
206
+ args: Parsed arguments containing search expression
207
+ width: Terminal width for formatting
208
+
209
+ Returns:
210
+ Formatted header string
211
+ """
212
+ rule = '-' * width if self.FORMATTING['show_rules'] else ''
213
+ total = len(commons + propers)
214
+ expr = self.sanitize(args.expression)
215
+
216
+ breakdown = ''
217
+ if args.include_proper and not args.only_proper:
218
+ breakdown = ' (' + str(len(propers)) + ' proper, ' + str(len(commons)) + ' common)'
219
+
220
+ if self.FORMATTING['show_rules']:
221
+ txt = "{rule}\n{total} matches for /{expr}/{breakdown}\n{rule}"
222
+ else:
223
+ txt = "{total} matches for /{expr}/{breakdown}"
224
+
225
+ return txt.format(rule=rule, total=total, expr=expr, breakdown=breakdown)
226
+
227
+ def formatted(self, words: List[str], termwidth: int, termheight: int) -> str:
228
+ """Format word list into columns that fit the terminal dimensions.
229
+
230
+ Args:
231
+ words: List of words to format
232
+ termwidth: Width of terminal in characters
233
+ termheight: Height of terminal in characters
234
+
235
+ Returns:
236
+ String of words formatted in columns
237
+ """
238
+ N = len(words)
239
+ GUTTER = self.FORMATTING['gutter_width']
240
+ COLWIDTH = max([len(word) for word in words])
241
+
242
+ # Adjust terminal width by margin
243
+ termwidth = termwidth - self.FORMATTING['terminal_margin']
244
+
245
+ # The maximum number of columns that will fit in the terminal
246
+ # (taking into account gutter width). The mathematics:
247
+ # termwidth = n*colwidth + (n-1)*gutter, solve for n.
248
+ max_num_cols = max(1, floor((GUTTER + termwidth)/float(GUTTER + COLWIDTH)))
249
+
250
+ # These are heuristics for determining the number of columns of output.
251
+ num_cols = max(1, min(max_num_cols, ceil(N/(termheight/2.))))
252
+ num_rows = ceil(N/float(num_cols))
253
+
254
+ # Convert to integers.
255
+ num_cols = int(num_cols)
256
+ num_rows = int(num_rows)
257
+
258
+ # Fill in the rest of the matrix with blanks.
259
+ # words = words + ['']*((numcols - (n-numcols*numrows)) % numcols)
260
+ words = words + ['']*(num_cols*num_rows - N)
261
+
262
+ rowlist = []
263
+ for k in range(num_rows):
264
+ rowlist += [''.join(word.ljust(COLWIDTH+GUTTER) \
265
+ for word in words[k::num_rows]).rstrip()]
266
+
267
+ pretty_printed_results = '\n'.join(rowlist)
268
+ return pretty_printed_results
269
+
270
+
271
+ # This is invoked when there's an error parsing the wordlist files
272
+ class LexitronParseError(Exception):
273
+ pass
274
+
275
+
276
+ # The function that will be called from the command line
277
+ def main():
278
+ """Entry point for the Lexitron command line interface."""
279
+ lx = Lexitron()
280
+ args = lx.parse_args(sys.argv[1:])
281
+ matches = lx.search(args)
282
+ lx.print_results(matches, args)
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "Lexitron"
7
+ version = "2.1.2"
8
+ authors = [
9
+ {name = "Rothos"}
10
+ ]
11
+ description = "A command-line regex search engine for the English language"
12
+ readme = "README.md"
13
+ requires-python = ">=3.7"
14
+ keywords = ["english", "dictionary", "crossword", "spelling", "words", "lexicon", "search"]
15
+ license = {text = "GNU GPL Version 2"}
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ ]
19
+ dependencies = [
20
+ "importlib-metadata;python_version<'3.8'",
21
+ "importlib-resources;python_version<'3.8'"
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "http://github.com/rothos/lexitron"
26
+
27
+ [tool.setuptools]
28
+ zip-safe = false
29
+ include-package-data = true
30
+ package-dir = {""= "."}
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["."]
34
+
35
+ [tool.setuptools.package-data]
36
+ "*" = ["*.txt"]
37
+
38
+ [project.scripts]
39
+ lx = "lexitron.lexitron:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -1,190 +0,0 @@
1
- import sys, os, re, argparse
2
- from importlib import resources, metadata
3
- from math import ceil, floor
4
-
5
- class Lexitron:
6
- def __init__(self):
7
- # The wordlist files
8
- self.wordlist_common_file = resources.files('lexitron').joinpath('agid-common.txt')
9
- self.wordlist_proper_file = resources.files('lexitron').joinpath('agid-proper.txt')
10
-
11
- with self.wordlist_common_file.open('rb') as f:
12
- self.wordlist_common = f.read()
13
- f.close()
14
-
15
- with self.wordlist_proper_file.open('rb') as f:
16
- self.wordlist_proper = f.read()
17
- f.close()
18
-
19
- # Here is the argument parser
20
- description = "Lexitron, a regex search engine for the English " +\
21
- "language."
22
- epilog = "Currently, Lexitron only allows case-insensitive " +\
23
- "searching.\n\nSee <http://github.com/rothos/lexitron> for " +\
24
- "further documentation and examples."
25
- self.parser = argparse.ArgumentParser(prog='lx',
26
- description=description, epilog=epilog)
27
-
28
- # Add the positional argument
29
- self.parser.add_argument('expression', type=str,
30
- help='The regular expression to search')
31
-
32
- # Add the optional arguments
33
- # self.parser.add_argument('-a',
34
- # dest='all', action='store_true',
35
- # help='Search all words, including plurals and conjugations')
36
- self.parser.add_argument('-n', '--number-only',
37
- dest='number', action='store_true',
38
- help='Print only the number of matches')
39
- self.parser.add_argument('-u', '--lowercase',
40
- dest='only_common', action='store_true',
41
- help='Search only for lowercase/common/uncapitalized words (like "boat")')
42
- self.parser.add_argument('-U', '--uppercase',
43
- dest='only_proper', action='store_true',
44
- help='Search only for uppercase/proper/capitalized words (like "France")')
45
- self.parser.add_argument('-v', '--version',
46
- action='version', version="Lexitron "+metadata.version('lexitron'),
47
- help='Show version and exit')
48
- self.parser.add_argument('-x', '--plain',
49
- dest='unformatted', action='store_true',
50
- help='Print unformatted output, one word per line')
51
-
52
- def print_help(self):
53
- self.parser.print_help()
54
-
55
- def parse_args(self, argv):
56
- # Parse the incoming arguments
57
- args = self.parser.parse_args(argv)
58
- return args
59
-
60
- def search(self, args):
61
- # Figure out which files to search
62
- if args.only_common and args.only_proper:
63
- msg = 'Mutually exclusive options -u and -U may not ' \
64
- + 'be used together.'
65
- raise LexitronOptionsError(msg)
66
-
67
- # Get the expression ready.
68
- expr = self.sanitize(args.expression)
69
-
70
- # Currently, lexitron search is case-insensitive.
71
- expr = re.compile(expr, flags=re.IGNORECASE)
72
- matches = {'common': [], 'proper': []}
73
-
74
- # Now open each wordlist and start searching.
75
- if not args.only_proper:
76
- for line in self.wordlist_common.splitlines():
77
- word = self.parse_line(line)
78
- if expr.search(word):
79
- matches['common'] += [word]
80
-
81
- if not args.only_common:
82
- for line in self.wordlist_proper.splitlines():
83
- word = self.parse_line(line)
84
- if expr.search(word):
85
- matches['proper'] += [word]
86
-
87
- matches['common'] = sorted(list(set(matches['common'])))
88
- matches['proper'] = sorted(list(set(matches['proper'])))
89
-
90
- return matches
91
-
92
- def sanitize(self, expr):
93
- # Remove any wrapping quotation marks from the input.
94
- if expr[0] in ('"',"'"): expr = expr[1:]
95
- if expr[-1] in ('"',"'"): expr = expr[:-1]
96
- return expr
97
-
98
- def parse_line(self, line):
99
- # This only returns the word right now, but since the agid.txt list
100
- # contains other information (part of speech, derivative words, etc),
101
- # the search may become more robust in the future.
102
- return line.split()[0].decode()
103
-
104
- def print_results(self, matches, args):
105
- commons = matches['common']
106
- propers = matches['proper']
107
-
108
- if args.unformatted:
109
- if args.number:
110
- # Unformatted number
111
- print(str(len(propers + commons)))
112
- else:
113
- # Unformatted matches
114
- print('\n'.join(propers + commons))
115
-
116
- else:
117
- width, height = os.get_terminal_size()
118
- width = width - 1 # Wiggle room
119
-
120
- if args.number:
121
- # Formatted number
122
- print(self.header(commons, propers, args, width))
123
- else:
124
- # Formatted matches
125
- print(self.header(commons, propers, args, width))
126
- if propers:
127
- print(self.formatted(propers, width, height))
128
- if commons:
129
- print(self.formatted(commons, width, height))
130
-
131
- def header(self, commons, propers, args, width):
132
- rule = '-' * width
133
- total = len(commons + propers)
134
- expr = self.sanitize(args.expression)
135
-
136
- breakdown = [str(len(propers)) + ' proper', str(len(commons)) + ' common']
137
- if args.only_proper:
138
- breakdown = ['(restricted search)']
139
- elif args.only_common:
140
- breakdown = ['(restricted search)']
141
- breakdown = ' ~ '.join(breakdown)
142
-
143
- txt = "{rule}\n{total} results for /{expr}/\n" \
144
- "{breakdown}\n{rule}\n".format(rule=rule,
145
- total=total, expr=expr, breakdown=breakdown)
146
-
147
- return txt
148
-
149
- def formatted(self, words, termwidth, termheight):
150
- n = len(words)
151
- gutter = 4
152
- colwidth = max([len(word) for word in words])
153
-
154
- # The maximum number of columns that will fit in the terminal
155
- # (taking into account gutter width). The mathematics:
156
- # termwidth = n*colwidth + (n-1)*gutter, solve for n.
157
- maxnumcols = max(1, floor((gutter + termwidth)/float(gutter + colwidth)))
158
-
159
- # These are heuristics for determining the number of columns of output.
160
- numcols = max(1, min(maxnumcols, ceil(n/(termheight/2.))))
161
- numrows = ceil(n/float(numcols))
162
-
163
- # Convert to integers.
164
- numcols = int(numcols)
165
- numrows = int(numrows)
166
-
167
- # Fill in the rest of the matrix with blanks.
168
- # words = words + ['']*((numcols - (n-numcols*numrows)) % numcols)
169
- words = words + ['']*(numcols*numrows - n)
170
-
171
- rowlist = []
172
- for k in range(numrows):
173
- rowlist += [''.join(word.ljust(colwidth+gutter) \
174
- for word in words[k::numrows]).rstrip()]
175
-
176
- pretty_printed_results = '\n'.join(rowlist) + '\n'
177
- return pretty_printed_results
178
-
179
-
180
- # This is invoked when mutually exclusive options -a and -A are used together.
181
- class LexitronOptionsError(Exception):
182
- pass
183
-
184
-
185
- # The function that will be called from the command line
186
- def main():
187
- lx = Lexitron()
188
- args = lx.parse_args(sys.argv[1:])
189
- matches = lx.search(args)
190
- lx.print_results(matches, args)
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools"]
3
- build-backend = "setuptools.build_meta"
Lexitron-2.0.4/setup.cfg DELETED
@@ -1,38 +0,0 @@
1
- [metadata]
2
- name = Lexitron
3
- version = 2.0.4
4
- author = Rothos
5
- url = http://github.com/rothos/lexitron
6
- description = A command-line regex search engine for the English language
7
- long_description = file: README.md
8
- long_description_content_type = text/markdown
9
- keywords = english, dictionary, crossword, spelling, words, lexicon, search
10
- license = GNU GPL Version 2
11
- classifiers =
12
- Programming Language :: Python :: 3
13
-
14
- [options]
15
- zip_safe = False
16
- include_package_data = True
17
- packages = find:
18
- python_requires = >=3.7
19
- install_requires =
20
- importlib-metadata;python_version<'3.8'
21
- importlib-resources;python_version<'3.8'
22
- package_dir =
23
- = .
24
-
25
- [options.packages.find]
26
- where = .
27
-
28
- [options.package_data]
29
- * = *.txt
30
-
31
- [options.entry_points]
32
- console_scripts =
33
- lx = lexitron.lexitron:main
34
-
35
- [egg_info]
36
- tag_build =
37
- tag_date = 0
38
-
File without changes
File without changes