temporal-normalization-spacy 2.2.2__tar.gz → 2.3.0__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.
Files changed (23) hide show
  1. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/MANIFEST.in +1 -1
  2. {temporal_normalization_spacy-2.2.2/temporal_normalization_spacy.egg-info → temporal_normalization_spacy-2.3.0}/PKG-INFO +16 -15
  3. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/README.md +13 -14
  4. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/setup.py +3 -2
  5. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization/index.py +9 -1
  6. temporal_normalization_spacy-2.2.2/temporal_normalization/libs/temporal-normalization-2.1.0.jar → temporal_normalization_spacy-2.3.0/temporal_normalization/libs/temporal-normalization-2.2.0.jar +0 -0
  7. temporal_normalization_spacy-2.3.0/temporal_normalization/process/java_process.py +274 -0
  8. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0/temporal_normalization_spacy.egg-info}/PKG-INFO +16 -15
  9. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization_spacy.egg-info/SOURCES.txt +1 -1
  10. temporal_normalization_spacy-2.2.2/temporal_normalization/process/java_process.py +0 -176
  11. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/LICENSE +0 -0
  12. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/setup.cfg +0 -0
  13. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization/__init__.py +0 -0
  14. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization/commons/__init__.py +0 -0
  15. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization/commons/print_utils.py +0 -0
  16. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization/commons/temporal_models.py +0 -0
  17. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization/commons/temporal_types.py +0 -0
  18. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization/factory.py +0 -0
  19. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization/process/__init__.py +0 -0
  20. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization_spacy.egg-info/dependency_links.txt +0 -0
  21. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization_spacy.egg-info/entry_points.txt +0 -0
  22. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization_spacy.egg-info/requires.txt +0 -0
  23. {temporal_normalization_spacy-2.2.2 → temporal_normalization_spacy-2.3.0}/temporal_normalization_spacy.egg-info/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  include README.md
2
- include temporal_normalization/libs/temporal-normalization-2.1.0.jar
2
+ include temporal_normalization/libs/temporal-normalization-2.2.0.jar
3
3
 
4
4
  recursive-include temporal_normalizer/commons *
5
5
  recursive-include temporal_normalizer/libs *
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: temporal_normalization_spacy
3
- Version: 2.2.2
3
+ Version: 2.3.0
4
4
  Summary: A spaCy plugin for temporal normalization and extraction of historical dates in Romanian narrative texts.
5
5
  Home-page: https://github.com/iliedorobat/timespan-normalization-spacy
6
6
  Author: Ilie Cristian Dorobat
7
+ Author-email: ic.dorobat@gmail.com
7
8
  Keywords: spacy,nlp,temporal normalization,timex,historical dates,romanian
8
9
  Classifier: Programming Language :: Python :: 3
9
10
  Classifier: Programming Language :: Python :: 3.9
@@ -19,6 +20,7 @@ Requires-Dist: spacy<4.0.0,>=3.8.7
19
20
  Requires-Dist: py4j>=0.10.9.9
20
21
  Requires-Dist: langdetect>=1.0.9
21
22
  Dynamic: author
23
+ Dynamic: author-email
22
24
  Dynamic: classifier
23
25
  Dynamic: description
24
26
  Dynamic: description-content-type
@@ -78,12 +80,11 @@ quality and reliability of date-related information extracted from text.
78
80
 
79
81
 
80
82
  ## Getting started
81
- To integrate TeNs into spaCy pipelines you need the following:
82
83
 
83
84
  ### Prerequisites
84
85
  - Python 3.9-3.12
85
86
  - JRE 11-17
86
- - spaCy 3.x
87
+ - spaCy >=3.8.7,<4.0.0
87
88
  - py4j 0.10.9.9
88
89
  - langdetect 1.0.9
89
90
 
@@ -233,24 +234,24 @@ Start Edge:
233
234
  Matched value: Sec al II-lea a.ch.
234
235
  Matched Type: century
235
236
  Normalized label: 2nd century BC
236
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
237
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
237
238
 
238
239
  End Edge:
239
240
  Matched value: Sec al II-lea a.ch.
240
241
  Matched Type: century
241
242
  Normalized label: 2nd century BC
242
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
243
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
243
244
 
244
245
  Periods:
245
246
  Matched value: Sec al II-lea a.ch.
246
247
  Matched Type: century
247
248
  Normalized label: 1st millennium BC
248
- DBpedia uri: https://dbpedia.org/page/1st_millennium_BC
249
+ DBpedia uri: https://dbpedia.org/resource/1st_millennium_BC
249
250
 
250
251
  Matched value: Sec al II-lea a.ch.
251
252
  Matched Type: century
252
253
  Normalized label: 2nd century BC
253
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
254
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
254
255
  ```
255
256
 
256
257
  ### Second Sentence
@@ -259,24 +260,24 @@ Start Edge:
259
260
  Matched value: secolul XX
260
261
  Matched Type: century
261
262
  Normalized label: 20th century
262
- DBpedia uri: https://dbpedia.org/page/20th_century
263
+ DBpedia uri: https://dbpedia.org/resource/20th_century
263
264
 
264
265
  End Edge:
265
266
  Matched value: secolul XX
266
267
  Matched Type: century
267
268
  Normalized label: 20th century
268
- DBpedia uri: https://dbpedia.org/page/20th_century
269
+ DBpedia uri: https://dbpedia.org/resource/20th_century
269
270
 
270
271
  Periods:
271
272
  Matched value: secolul XX
272
273
  Matched Type: century
273
274
  Normalized label: 2nd millennium
274
- DBpedia uri: https://dbpedia.org/page/2nd_millennium
275
+ DBpedia uri: https://dbpedia.org/resource/2nd_millennium
275
276
 
276
277
  Matched value: secolul XX
277
278
  Matched Type: century
278
279
  Normalized label: 20th century
279
- DBpedia uri: https://dbpedia.org/page/20th_century
280
+ DBpedia uri: https://dbpedia.org/resource/20th_century
280
281
  ```
281
282
 
282
283
  ### Third Sentence
@@ -285,24 +286,24 @@ Start Edge:
285
286
  Matched value: Sec. 21
286
287
  Matched Type: century
287
288
  Normalized label: 21st century
288
- DBpedia uri: https://dbpedia.org/page/21st_century
289
+ DBpedia uri: https://dbpedia.org/resource/21st_century
289
290
 
290
291
  End Edge:
291
292
  Matched value: Sec. 21
292
293
  Matched Type: century
293
294
  Normalized label: 21st century
294
- DBpedia uri: https://dbpedia.org/page/21st_century
295
+ DBpedia uri: https://dbpedia.org/resource/21st_century
295
296
 
296
297
  Periods:
297
298
  Matched value: Sec. 21
298
299
  Matched Type: century
299
300
  Normalized label: 3rd millennium
300
- DBpedia uri: https://dbpedia.org/page/3rd_millennium
301
+ DBpedia uri: https://dbpedia.org/resource/3rd_millennium
301
302
 
302
303
  Matched value: Sec. 21
303
304
  Matched Type: century
304
305
  Normalized label: 21st century
305
- DBpedia uri: https://dbpedia.org/page/21st_century
306
+ DBpedia uri: https://dbpedia.org/resource/21st_century
306
307
  ```
307
308
 
308
309
 
@@ -47,12 +47,11 @@ quality and reliability of date-related information extracted from text.
47
47
 
48
48
 
49
49
  ## Getting started
50
- To integrate TeNs into spaCy pipelines you need the following:
51
50
 
52
51
  ### Prerequisites
53
52
  - Python 3.9-3.12
54
53
  - JRE 11-17
55
- - spaCy 3.x
54
+ - spaCy >=3.8.7,<4.0.0
56
55
  - py4j 0.10.9.9
57
56
  - langdetect 1.0.9
58
57
 
@@ -202,24 +201,24 @@ Start Edge:
202
201
  Matched value: Sec al II-lea a.ch.
203
202
  Matched Type: century
204
203
  Normalized label: 2nd century BC
205
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
204
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
206
205
 
207
206
  End Edge:
208
207
  Matched value: Sec al II-lea a.ch.
209
208
  Matched Type: century
210
209
  Normalized label: 2nd century BC
211
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
210
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
212
211
 
213
212
  Periods:
214
213
  Matched value: Sec al II-lea a.ch.
215
214
  Matched Type: century
216
215
  Normalized label: 1st millennium BC
217
- DBpedia uri: https://dbpedia.org/page/1st_millennium_BC
216
+ DBpedia uri: https://dbpedia.org/resource/1st_millennium_BC
218
217
 
219
218
  Matched value: Sec al II-lea a.ch.
220
219
  Matched Type: century
221
220
  Normalized label: 2nd century BC
222
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
221
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
223
222
  ```
224
223
 
225
224
  ### Second Sentence
@@ -228,24 +227,24 @@ Start Edge:
228
227
  Matched value: secolul XX
229
228
  Matched Type: century
230
229
  Normalized label: 20th century
231
- DBpedia uri: https://dbpedia.org/page/20th_century
230
+ DBpedia uri: https://dbpedia.org/resource/20th_century
232
231
 
233
232
  End Edge:
234
233
  Matched value: secolul XX
235
234
  Matched Type: century
236
235
  Normalized label: 20th century
237
- DBpedia uri: https://dbpedia.org/page/20th_century
236
+ DBpedia uri: https://dbpedia.org/resource/20th_century
238
237
 
239
238
  Periods:
240
239
  Matched value: secolul XX
241
240
  Matched Type: century
242
241
  Normalized label: 2nd millennium
243
- DBpedia uri: https://dbpedia.org/page/2nd_millennium
242
+ DBpedia uri: https://dbpedia.org/resource/2nd_millennium
244
243
 
245
244
  Matched value: secolul XX
246
245
  Matched Type: century
247
246
  Normalized label: 20th century
248
- DBpedia uri: https://dbpedia.org/page/20th_century
247
+ DBpedia uri: https://dbpedia.org/resource/20th_century
249
248
  ```
250
249
 
251
250
  ### Third Sentence
@@ -254,24 +253,24 @@ Start Edge:
254
253
  Matched value: Sec. 21
255
254
  Matched Type: century
256
255
  Normalized label: 21st century
257
- DBpedia uri: https://dbpedia.org/page/21st_century
256
+ DBpedia uri: https://dbpedia.org/resource/21st_century
258
257
 
259
258
  End Edge:
260
259
  Matched value: Sec. 21
261
260
  Matched Type: century
262
261
  Normalized label: 21st century
263
- DBpedia uri: https://dbpedia.org/page/21st_century
262
+ DBpedia uri: https://dbpedia.org/resource/21st_century
264
263
 
265
264
  Periods:
266
265
  Matched value: Sec. 21
267
266
  Matched Type: century
268
267
  Normalized label: 3rd millennium
269
- DBpedia uri: https://dbpedia.org/page/3rd_millennium
268
+ DBpedia uri: https://dbpedia.org/resource/3rd_millennium
270
269
 
271
270
  Matched value: Sec. 21
272
271
  Matched Type: century
273
272
  Normalized label: 21st century
274
- DBpedia uri: https://dbpedia.org/page/21st_century
273
+ DBpedia uri: https://dbpedia.org/resource/21st_century
275
274
  ```
276
275
 
277
276
 
@@ -2,8 +2,9 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="temporal_normalization_spacy",
5
- version="2.2.2",
5
+ version="2.3.0",
6
6
  author="Ilie Cristian Dorobat",
7
+ author_email="ic.dorobat@gmail.com",
7
8
  description="A spaCy plugin for temporal normalization and extraction of "
8
9
  "historical dates in Romanian narrative texts.",
9
10
  keywords="spacy, nlp, temporal normalization, timex, historical dates, romanian",
@@ -18,7 +19,7 @@ setup(
18
19
  ],
19
20
  },
20
21
  package_data={
21
- "temporal_normalization.libs": ["temporal-normalization-2.1.0.jar"],
22
+ "temporal_normalization.libs": ["temporal-normalization-2.2.0.jar"],
22
23
  },
23
24
  install_requires=[
24
25
  "spacy>=3.8.7,<4.0.0",
@@ -43,6 +43,11 @@ class TemporalNormalization:
43
43
  self.nlp = nlp
44
44
  self.count = 0
45
45
 
46
+ # Initialize connection attributes before starting the Java gateway, as initialization may fail
47
+ # (e.g., during Flask's debug reloader), and __del__() may still be called on a partial instance.
48
+ self.java_process = None
49
+ self.gateway = None
50
+
46
51
  root_path = str(Path(__file__).resolve().parent.parent)
47
52
  java_process, gateway = start_conn(root_path)
48
53
  self.java_process: subprocess.Popen = java_process
@@ -97,7 +102,10 @@ class TemporalNormalization:
97
102
  network sockets that might otherwise persist after the Python process ends.
98
103
  """
99
104
 
100
- close_conn(self.java_process, self.gateway)
105
+ if self.java_process is not None or self.gateway is not None:
106
+ close_conn(self.java_process, self.gateway)
107
+ else:
108
+ print(f"⚠️ No Java process or Python connection to close.")
101
109
 
102
110
 
103
111
  def _prepare_str_patterns(expressions: list[TemporalExpression]) -> list[str]:
@@ -0,0 +1,274 @@
1
+ import io
2
+ import re
3
+ import shutil
4
+ import subprocess
5
+ import threading
6
+ import time
7
+
8
+ from py4j.java_gateway import JavaGateway, GatewayParameters
9
+ from py4j.protocol import Py4JNetworkError
10
+
11
+ from temporal_normalization.commons.print_utils import console
12
+
13
+
14
+ def start_conn(root_path: str) -> tuple[subprocess.Popen, JavaGateway]:
15
+ """
16
+ Starts the Java temporal normalization process and establishes a Py4J gateway connection.
17
+
18
+ Args:
19
+ root_path (str): The root directory of the project.
20
+
21
+ Returns:
22
+ tuple[subprocess.Popen, JavaGateway]:
23
+ - The subprocess.Popen object representing the running Java process.
24
+ - The JavaGateway object representing the active Py4J connection.
25
+
26
+ Note:
27
+ - Requires Java 11 or higher to be installed and accessible in the system PATH.
28
+ - Requires `temporal-normalization-2.2.0.jar` to be present in the `libs` directory.
29
+ - The caller is responsible for closing the gateway and terminating the Java process
30
+ after usage to avoid orphaned processes.
31
+ """
32
+
33
+ check_java_version()
34
+
35
+ gateway_started = threading.Event()
36
+ gateway_error = threading.Event()
37
+
38
+ jar_path = (
39
+ f"{root_path}/temporal_normalization/libs/temporal-normalization-2.2.0.jar"
40
+ )
41
+
42
+ def stdout_callback(line: str):
43
+ if "Gateway Server Started" in line:
44
+ gateway_started.set()
45
+ print(line.strip())
46
+
47
+ stderr_lines = []
48
+ def stderr_callback(line: str):
49
+ stderr_lines.append(line)
50
+
51
+ if "Failed to bind" in line or "Address already in use" in line:
52
+ gateway_error.set()
53
+
54
+ java_process = subprocess.Popen(
55
+ ["java", "-jar", jar_path, "--python"],
56
+ stdout=subprocess.PIPE,
57
+ stderr=subprocess.PIPE,
58
+ text=True,
59
+ )
60
+
61
+ threading.Thread(target=drain_stream, args=(java_process.stdout, stdout_callback), daemon=True).start()
62
+ threading.Thread(target=drain_stream, args=(java_process.stderr, stderr_callback), daemon=True).start()
63
+
64
+ timeout = 10.0
65
+ start_time = time.monotonic()
66
+
67
+ while not gateway_started.is_set():
68
+ if gateway_error.wait(timeout=0.1):
69
+ error = "\n\t".join(stderr_lines)
70
+
71
+ if java_process.poll() is None:
72
+ java_process.terminate()
73
+
74
+ raise RuntimeError(
75
+ "Java Gateway failed to start.\n"
76
+ f"Java stderr:\n\t{error}"
77
+ )
78
+
79
+ if time.monotonic() - start_time >= timeout:
80
+ error = "\n\t".join(stderr_lines)
81
+
82
+ java_process.terminate()
83
+ java_process.wait()
84
+
85
+ raise RuntimeError(
86
+ f"Java Gateway did not start within 10 seconds.\n"
87
+ f"Java stderr:\n\t{error}"
88
+ )
89
+
90
+ time.sleep(0.1)
91
+
92
+ gateway = JavaGateway(
93
+ gateway_parameters=GatewayParameters(auto_convert=True, read_timeout=None),
94
+ callback_server_parameters=None,
95
+ )
96
+
97
+ print("Python connection established.")
98
+
99
+ return java_process, gateway
100
+
101
+
102
+ def close_conn(java_process: subprocess.Popen | None, gateway: JavaGateway | None) -> None:
103
+ """
104
+ Closes the Py4J gateway connection and terminates the associated Java process.
105
+
106
+ This function performs the shutdown sequence by:
107
+ 1. Attempting to gracefully close the Py4J gateway connection.
108
+ - If the Java process has already terminated, the resulting
109
+ Py4JNetworkError is handled without raising an exception.
110
+ 2. Terminating the Java process if it is still running.
111
+ 3. Reporting the shutdown status for each resource.
112
+
113
+ Args:
114
+ java_process (subprocess.Popen | None): The Java process launched
115
+ with subprocess, or None if the process was not successfully
116
+ initialized.
117
+ gateway (JavaGateway | None): The active Py4J gateway connection,
118
+ or None if the gateway was not successfully initialized.
119
+
120
+ Notes:
121
+ - Call this function once all interactions with the Java process
122
+ have been completed.
123
+ - It is safe to call even if either the gateway or Java process
124
+ has already been closed or was not successfully initialized.
125
+ - The Py4J gateway is closed before the Java process is terminated.
126
+ """
127
+
128
+ _close_gateway_conn(gateway)
129
+ _close_java_conn(java_process)
130
+
131
+
132
+ def _close_gateway_conn(gateway: JavaGateway | None) -> None:
133
+ """
134
+ Closes the active Py4J gateway connection, if one exists.
135
+
136
+ Attempts to gracefully shut down the connection between Python and
137
+ the Java process. If the Java process has already terminated, the
138
+ resulting Py4JNetworkError is handled and reported without raising
139
+ an exception.
140
+
141
+ Args:
142
+ gateway (JavaGateway | None): The active Py4J gateway connection,
143
+ or None if the connection was not successfully initialized.
144
+
145
+ Notes:
146
+ - It is safe to call this function when the gateway is None.
147
+ - It is safe to call this function after the Java process has
148
+ already terminated.
149
+ - Unexpected errors during gateway shutdown are caught and
150
+ reported rather than propagated.
151
+ """
152
+
153
+ if gateway is not None:
154
+ try:
155
+ # Proper way to shut down Py4J
156
+ gateway.shutdown()
157
+ print("✅ Python connection closed.")
158
+ except Py4JNetworkError:
159
+ print("⚠️ Java process already shut down.")
160
+ except Exception as e:
161
+ print(f"⚠️ Error shutting down gateway: {e}")
162
+ else:
163
+ print(f"⚠️ No Python connection to close.")
164
+
165
+
166
+ def _close_java_conn(java_process: subprocess.Popen | None) -> None:
167
+ """
168
+ Terminates the Java process, if it is still running.
169
+
170
+ Checks whether the Java process is active before attempting to
171
+ terminate it. If the process has already exited, no termination
172
+ is attempted and a status message is printed.
173
+
174
+ Args:
175
+ java_process (subprocess.Popen | None): The Java process started
176
+ with subprocess, or None if the process was not successfully
177
+ initialized.
178
+
179
+ Notes:
180
+ - It is safe to call this function when java_process is None.
181
+ - It is safe to call this function after the Java process has
182
+ already terminated.
183
+ - If the process is still running, terminate() is called and the
184
+ function waits for the process to exit.
185
+ """
186
+
187
+ if java_process is None:
188
+ print("⚠️ No Java process to close.")
189
+ return
190
+
191
+ if java_process.poll() is None:
192
+ # Terminate Java process
193
+ java_process.terminate()
194
+ java_process.wait()
195
+ print("✅ Java process terminated.")
196
+ else:
197
+ print("⚠️ Java process already terminated.")
198
+
199
+
200
+ def drain_stream(stream: io.TextIOBase, callback=None) -> None:
201
+ """
202
+ Consumes the output from a given stream until a specific marker is found,
203
+ then closes the stream.
204
+
205
+ This function is typically used to monitor the stdout or stderr of a subprocess
206
+ (e.g., a Java process started from Python) and detect when a certain event occurs,
207
+ such as the initialization of a gateway server. Once the marker line is encountered,
208
+ the function prints it (or logs it) and terminates the stream reading.
209
+
210
+ Args:
211
+ stream (io.TextIOBase): A text-based stream object to read from, usually
212
+ subprocess.stdout or subprocess.stderr.
213
+ callback (callable, optional): A function that takes a single string argument (line).
214
+ It will be called for every line read from the stream.
215
+ Useful for detecting specific markers, logging, or
216
+ triggering events when certain output appears.
217
+
218
+ Raises:
219
+ AttributeError: If the provided `stream` does not have `readline` or `close` methods.
220
+ """
221
+
222
+ for line in iter(stream.readline, ""):
223
+ line = line.strip()
224
+ if callback:
225
+ callback(line)
226
+
227
+ stream.close()
228
+
229
+
230
+ def check_java_version() -> None:
231
+ """
232
+ Verifies that Java is installed and meets the minimum required version.
233
+
234
+ This function checks for the presence of the Java executable in the system PATH,
235
+ runs ``java -version``, and ensures that the version is at least 11. If Java is not
236
+ installed or the version is too low, it logs an error using ``console.error``.
237
+
238
+ Raises:
239
+ Logs error messages, but does not raise exceptions directly.
240
+ """
241
+
242
+ min_version = 11
243
+ java_path = shutil.which("java")
244
+
245
+ try:
246
+ if java_path:
247
+ # Run the command to check the Java version
248
+ result = subprocess.run(
249
+ [java_path, "-version"], capture_output=True, text=True
250
+ )
251
+
252
+ # Print the version information (Java version is printed to stderr)
253
+ if result.returncode == 0:
254
+ version_output = result.stderr
255
+ match = re.search(r'version "(\d+\.\d+)', version_output)
256
+
257
+ if match:
258
+ crr_version = float(match.group(1))
259
+ if crr_version < min_version:
260
+ console.error(
261
+ f"Java {crr_version} is installed, but version {min_version} is required." # noqa 501
262
+ )
263
+ else:
264
+ console.error("Could not extract Java version.")
265
+ else:
266
+ console.error("Error occurred while checking the version.")
267
+ else:
268
+ console.error("Java not found.")
269
+ except Exception as e:
270
+ console.error(e.__str__())
271
+
272
+
273
+ if __name__ == "__main__":
274
+ pass
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: temporal_normalization_spacy
3
- Version: 2.2.2
3
+ Version: 2.3.0
4
4
  Summary: A spaCy plugin for temporal normalization and extraction of historical dates in Romanian narrative texts.
5
5
  Home-page: https://github.com/iliedorobat/timespan-normalization-spacy
6
6
  Author: Ilie Cristian Dorobat
7
+ Author-email: ic.dorobat@gmail.com
7
8
  Keywords: spacy,nlp,temporal normalization,timex,historical dates,romanian
8
9
  Classifier: Programming Language :: Python :: 3
9
10
  Classifier: Programming Language :: Python :: 3.9
@@ -19,6 +20,7 @@ Requires-Dist: spacy<4.0.0,>=3.8.7
19
20
  Requires-Dist: py4j>=0.10.9.9
20
21
  Requires-Dist: langdetect>=1.0.9
21
22
  Dynamic: author
23
+ Dynamic: author-email
22
24
  Dynamic: classifier
23
25
  Dynamic: description
24
26
  Dynamic: description-content-type
@@ -78,12 +80,11 @@ quality and reliability of date-related information extracted from text.
78
80
 
79
81
 
80
82
  ## Getting started
81
- To integrate TeNs into spaCy pipelines you need the following:
82
83
 
83
84
  ### Prerequisites
84
85
  - Python 3.9-3.12
85
86
  - JRE 11-17
86
- - spaCy 3.x
87
+ - spaCy >=3.8.7,<4.0.0
87
88
  - py4j 0.10.9.9
88
89
  - langdetect 1.0.9
89
90
 
@@ -233,24 +234,24 @@ Start Edge:
233
234
  Matched value: Sec al II-lea a.ch.
234
235
  Matched Type: century
235
236
  Normalized label: 2nd century BC
236
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
237
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
237
238
 
238
239
  End Edge:
239
240
  Matched value: Sec al II-lea a.ch.
240
241
  Matched Type: century
241
242
  Normalized label: 2nd century BC
242
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
243
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
243
244
 
244
245
  Periods:
245
246
  Matched value: Sec al II-lea a.ch.
246
247
  Matched Type: century
247
248
  Normalized label: 1st millennium BC
248
- DBpedia uri: https://dbpedia.org/page/1st_millennium_BC
249
+ DBpedia uri: https://dbpedia.org/resource/1st_millennium_BC
249
250
 
250
251
  Matched value: Sec al II-lea a.ch.
251
252
  Matched Type: century
252
253
  Normalized label: 2nd century BC
253
- DBpedia uri: https://dbpedia.org/page/2nd_century_BC
254
+ DBpedia uri: https://dbpedia.org/resource/2nd_century_BC
254
255
  ```
255
256
 
256
257
  ### Second Sentence
@@ -259,24 +260,24 @@ Start Edge:
259
260
  Matched value: secolul XX
260
261
  Matched Type: century
261
262
  Normalized label: 20th century
262
- DBpedia uri: https://dbpedia.org/page/20th_century
263
+ DBpedia uri: https://dbpedia.org/resource/20th_century
263
264
 
264
265
  End Edge:
265
266
  Matched value: secolul XX
266
267
  Matched Type: century
267
268
  Normalized label: 20th century
268
- DBpedia uri: https://dbpedia.org/page/20th_century
269
+ DBpedia uri: https://dbpedia.org/resource/20th_century
269
270
 
270
271
  Periods:
271
272
  Matched value: secolul XX
272
273
  Matched Type: century
273
274
  Normalized label: 2nd millennium
274
- DBpedia uri: https://dbpedia.org/page/2nd_millennium
275
+ DBpedia uri: https://dbpedia.org/resource/2nd_millennium
275
276
 
276
277
  Matched value: secolul XX
277
278
  Matched Type: century
278
279
  Normalized label: 20th century
279
- DBpedia uri: https://dbpedia.org/page/20th_century
280
+ DBpedia uri: https://dbpedia.org/resource/20th_century
280
281
  ```
281
282
 
282
283
  ### Third Sentence
@@ -285,24 +286,24 @@ Start Edge:
285
286
  Matched value: Sec. 21
286
287
  Matched Type: century
287
288
  Normalized label: 21st century
288
- DBpedia uri: https://dbpedia.org/page/21st_century
289
+ DBpedia uri: https://dbpedia.org/resource/21st_century
289
290
 
290
291
  End Edge:
291
292
  Matched value: Sec. 21
292
293
  Matched Type: century
293
294
  Normalized label: 21st century
294
- DBpedia uri: https://dbpedia.org/page/21st_century
295
+ DBpedia uri: https://dbpedia.org/resource/21st_century
295
296
 
296
297
  Periods:
297
298
  Matched value: Sec. 21
298
299
  Matched Type: century
299
300
  Normalized label: 3rd millennium
300
- DBpedia uri: https://dbpedia.org/page/3rd_millennium
301
+ DBpedia uri: https://dbpedia.org/resource/3rd_millennium
301
302
 
302
303
  Matched value: Sec. 21
303
304
  Matched Type: century
304
305
  Normalized label: 21st century
305
- DBpedia uri: https://dbpedia.org/page/21st_century
306
+ DBpedia uri: https://dbpedia.org/resource/21st_century
306
307
  ```
307
308
 
308
309
 
@@ -9,7 +9,7 @@ temporal_normalization/commons/__init__.py
9
9
  temporal_normalization/commons/print_utils.py
10
10
  temporal_normalization/commons/temporal_models.py
11
11
  temporal_normalization/commons/temporal_types.py
12
- temporal_normalization/libs/temporal-normalization-2.1.0.jar
12
+ temporal_normalization/libs/temporal-normalization-2.2.0.jar
13
13
  temporal_normalization/process/__init__.py
14
14
  temporal_normalization/process/java_process.py
15
15
  temporal_normalization_spacy.egg-info/PKG-INFO
@@ -1,176 +0,0 @@
1
- import io
2
- import re
3
- import shutil
4
- import subprocess
5
- import threading
6
-
7
- from py4j.java_gateway import JavaGateway, GatewayParameters, CallbackServerParameters
8
- from py4j.protocol import Py4JNetworkError
9
-
10
- from temporal_normalization.commons.print_utils import console
11
-
12
-
13
- gateway_started = threading.Event()
14
-
15
- def start_conn(root_path: str) -> tuple[subprocess.Popen, JavaGateway]:
16
- """
17
- Starts the Java temporal normalization process and establishes a Py4J gateway connection.
18
-
19
- Args:
20
- root_path (str): The root directory of the project.
21
-
22
- Returns:
23
- tuple[subprocess.Popen, JavaGateway]:
24
- - The subprocess.Popen object representing the running Java process.
25
- - The JavaGateway object representing the active Py4J connection.
26
-
27
- Note:
28
- - Requires Java 11 or higher to be installed and accessible in the system PATH.
29
- - Requires `temporal-normalization-2.1.0.jar` to be present in the `libs` directory.
30
- - The caller is responsible for closing the gateway and terminating the Java process
31
- after usage to avoid orphaned processes.
32
- """
33
-
34
- check_java_version()
35
-
36
- jar_path = (
37
- f"{root_path}/temporal_normalization/libs/temporal-normalization-2.1.0.jar"
38
- )
39
-
40
- def stdout_callback(line: str):
41
- if "Gateway Server Started" in line:
42
- gateway_started.set()
43
- print(line.strip())
44
-
45
- java_process = subprocess.Popen(
46
- ["java", "-jar", jar_path, "--python"],
47
- stdout=subprocess.PIPE,
48
- stderr=subprocess.PIPE,
49
- text=True,
50
- )
51
-
52
- threading.Thread(target=drain_stream, args=(java_process.stdout, stdout_callback), daemon=True).start()
53
- threading.Thread(target=drain_stream, args=(java_process.stderr,), daemon=True).start()
54
-
55
- if not gateway_started.wait(timeout=10.0):
56
- java_process.terminate()
57
- raise RuntimeError("Java Gateway did not start within 10 seconds")
58
-
59
- gateway = JavaGateway(
60
- gateway_parameters=GatewayParameters(auto_convert=True, read_timeout=None),
61
- callback_server_parameters=None,
62
- )
63
-
64
- print("Python connection established.")
65
-
66
- return java_process, gateway
67
-
68
-
69
- def close_conn(java_process: subprocess.Popen, gateway: JavaGateway) -> None:
70
- """
71
- Closes the active connection between Python and the Java process started via Py4J.
72
-
73
- This function ensures a proper shutdown sequence:
74
- 1. Attempts to gracefully shut down the Py4J gateway connection.
75
- - If the Java process is already closed, a Py4JNetworkError is caught and logged.
76
- 2. Terminates the underlying Java process.
77
- 3. Prints status messages for debugging/confirmation.
78
-
79
- Args:
80
- java_process (subprocess.Popen): The Java process launched with subprocess.
81
- gateway (JavaGateway): The active Py4J gateway connection.
82
-
83
- Notes:
84
- - Call this function once you have finished all interactions with the Java process.
85
- - It is safe to call even if the Java process has already exited.
86
- """
87
-
88
- try:
89
- # Proper way to shut down Py4J
90
- gateway.shutdown()
91
- print("✅ Python connection closed.")
92
- except Py4JNetworkError:
93
- print("⚠️ Java process already shut down.")
94
- except Exception as e:
95
- print(f"⚠️ Error shutting down gateway: {e}")
96
-
97
- # Terminate Java process
98
- java_process.terminate()
99
- java_process.wait()
100
- print("✅ Java process terminated.")
101
-
102
-
103
- def drain_stream(stream: io.TextIOBase, callback=None) -> None:
104
- """
105
- Consumes the output from a given stream until a specific marker is found,
106
- then closes the stream.
107
-
108
- This function is typically used to monitor the stdout or stderr of a subprocess
109
- (e.g., a Java process started from Python) and detect when a certain event occurs,
110
- such as the initialization of a gateway server. Once the marker line is encountered,
111
- the function prints it (or logs it) and terminates the stream reading.
112
-
113
- Args:
114
- stream (io.TextIOBase): A text-based stream object to read from, usually
115
- subprocess.stdout or subprocess.stderr.
116
- callback (callable, optional): A function that takes a single string argument (line).
117
- It will be called for every line read from the stream.
118
- Useful for detecting specific markers, logging, or
119
- triggering events when certain output appears.
120
-
121
- Raises:
122
- AttributeError: If the provided `stream` does not have `readline` or `close` methods.
123
- """
124
- for line in iter(stream.readline, ""):
125
- line = line.strip()
126
- if callback:
127
- callback(line)
128
-
129
- stream.close()
130
-
131
-
132
- def check_java_version() -> None:
133
- """
134
- Verifies that Java is installed and meets the minimum required version.
135
-
136
- This function checks for the presence of the Java executable in the system PATH,
137
- runs ``java -version``, and ensures that the version is at least 11. If Java is not
138
- installed or the version is too low, it logs an error using ``console.error``.
139
-
140
- Raises:
141
- Logs error messages, but does not raise exceptions directly.
142
- """
143
-
144
- min_version = 11
145
- java_path = shutil.which("java")
146
-
147
- try:
148
- if java_path:
149
- # Run the command to check the Java version
150
- result = subprocess.run(
151
- [java_path, "-version"], capture_output=True, text=True
152
- )
153
-
154
- # Print the version information (Java version is printed to stderr)
155
- if result.returncode == 0:
156
- version_output = result.stderr
157
- match = re.search(r'version "(\d+\.\d+)', version_output)
158
-
159
- if match:
160
- crr_version = float(match.group(1))
161
- if crr_version < min_version:
162
- console.error(
163
- f"Java {crr_version} is installed, but version {min_version} is required." # noqa 501
164
- )
165
- else:
166
- console.error("Could not extract Java version.")
167
- else:
168
- console.error("Error occurred while checking the version.")
169
- else:
170
- console.error("Java not found.")
171
- except Exception as e:
172
- console.error(e.__str__())
173
-
174
-
175
- if __name__ == "__main__":
176
- pass