kernpy 0.0.2__py3-none-any.whl → 1.0.1__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.
Files changed (56) hide show
  1. kernpy/__init__.py +30 -0
  2. kernpy/__main__.py +127 -0
  3. kernpy/core/__init__.py +119 -0
  4. kernpy/core/_io.py +48 -0
  5. kernpy/core/base_antlr_importer.py +61 -0
  6. kernpy/core/base_antlr_spine_parser_listener.py +196 -0
  7. kernpy/core/basic_spine_importer.py +43 -0
  8. kernpy/core/document.py +965 -0
  9. kernpy/core/dyn_importer.py +30 -0
  10. kernpy/core/dynam_spine_importer.py +42 -0
  11. kernpy/core/error_listener.py +51 -0
  12. kernpy/core/exporter.py +535 -0
  13. kernpy/core/fing_spine_importer.py +42 -0
  14. kernpy/core/generated/kernSpineLexer.interp +444 -0
  15. kernpy/core/generated/kernSpineLexer.py +535 -0
  16. kernpy/core/generated/kernSpineLexer.tokens +236 -0
  17. kernpy/core/generated/kernSpineParser.interp +425 -0
  18. kernpy/core/generated/kernSpineParser.py +9954 -0
  19. kernpy/core/generated/kernSpineParser.tokens +236 -0
  20. kernpy/core/generated/kernSpineParserListener.py +1200 -0
  21. kernpy/core/generated/kernSpineParserVisitor.py +673 -0
  22. kernpy/core/generic.py +426 -0
  23. kernpy/core/gkern.py +526 -0
  24. kernpy/core/graphviz_exporter.py +89 -0
  25. kernpy/core/harm_spine_importer.py +41 -0
  26. kernpy/core/import_humdrum_old.py +853 -0
  27. kernpy/core/importer.py +285 -0
  28. kernpy/core/importer_factory.py +43 -0
  29. kernpy/core/kern_spine_importer.py +73 -0
  30. kernpy/core/mens_spine_importer.py +23 -0
  31. kernpy/core/mhxm_spine_importer.py +44 -0
  32. kernpy/core/pitch_models.py +338 -0
  33. kernpy/core/root_spine_importer.py +58 -0
  34. kernpy/core/spine_importer.py +45 -0
  35. kernpy/core/text_spine_importer.py +43 -0
  36. kernpy/core/tokenizers.py +239 -0
  37. kernpy/core/tokens.py +2011 -0
  38. kernpy/core/transposer.py +300 -0
  39. kernpy/io/__init__.py +14 -0
  40. kernpy/io/public.py +355 -0
  41. kernpy/polish_scores/__init__.py +13 -0
  42. kernpy/polish_scores/download_polish_dataset.py +357 -0
  43. kernpy/polish_scores/iiif.py +47 -0
  44. kernpy/test_grammar.sh +22 -0
  45. kernpy/util/__init__.py +14 -0
  46. kernpy/util/helpers.py +55 -0
  47. kernpy/util/store_cache.py +35 -0
  48. kernpy/visualize_analysis.sh +23 -0
  49. kernpy-1.0.1.dist-info/METADATA +497 -0
  50. kernpy-1.0.1.dist-info/RECORD +51 -0
  51. {kernpy-0.0.2.dist-info → kernpy-1.0.1.dist-info}/WHEEL +1 -2
  52. kernpy/example.py +0 -1
  53. kernpy-0.0.2.dist-info/LICENSE +0 -19
  54. kernpy-0.0.2.dist-info/METADATA +0 -19
  55. kernpy-0.0.2.dist-info/RECORD +0 -7
  56. kernpy-0.0.2.dist-info/top_level.txt +0 -1
kernpy/core/generic.py ADDED
@@ -0,0 +1,426 @@
1
+ """
2
+ Public API for KernPy.
3
+
4
+ The main functions for handling the input and output of **kern files are provided here.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+ from typing import List, Optional, Any, Union, Tuple
10
+ from collections.abc import Sequence
11
+
12
+ from kernpy.core import Importer, Document, Exporter, ExportOptions, GraphvizExporter, TokenCategoryHierarchyMapper
13
+ from kernpy.core._io import _write
14
+ from kernpy.util.helpers import deprecated
15
+
16
+
17
+ class Generic:
18
+ """
19
+ Generic class.
20
+
21
+ This class provides support to the public API for KernPy.
22
+
23
+ The main functions implementation are provided here.
24
+ """
25
+
26
+ @classmethod
27
+ def read(
28
+ cls,
29
+ path: Path,
30
+ strict: Optional[bool] = False
31
+ ) -> (Document, List[str]):
32
+ """
33
+
34
+ Args:
35
+ path:
36
+ strict:
37
+
38
+ Returns:
39
+
40
+ """
41
+ importer = Importer()
42
+ document = importer.import_file(path)
43
+ errors = importer.errors
44
+
45
+ if strict and len(errors) > 0:
46
+ raise Exception(importer.get_error_messages())
47
+
48
+ return document, errors
49
+
50
+ @classmethod
51
+ def create(
52
+ cls,
53
+ content: str,
54
+ strict: Optional[bool] = False
55
+ ) -> (Document, List[str]):
56
+ """
57
+
58
+ Args:
59
+ content:
60
+ strict:
61
+
62
+ Returns:
63
+
64
+ """
65
+ importer = Importer()
66
+ document = importer.import_string(content)
67
+ errors = importer.errors
68
+
69
+ if strict and len(errors) > 0:
70
+ raise Exception(importer.get_error_messages())
71
+
72
+ return document, errors
73
+
74
+ @classmethod
75
+ def export(
76
+ cls,
77
+ document: Document,
78
+ options: ExportOptions
79
+ ) -> str:
80
+ """
81
+
82
+ Args:
83
+ document:
84
+ options:
85
+
86
+ Returns:
87
+
88
+ """
89
+ exporter = Exporter()
90
+ return exporter.export_string(document, options)
91
+
92
+ @classmethod
93
+ def store(
94
+ cls,
95
+ document: Document,
96
+ path: Path,
97
+ options: ExportOptions
98
+ ) -> None:
99
+ """
100
+
101
+ Args:
102
+ document:
103
+ path:
104
+ options:
105
+
106
+ Returns:
107
+ """
108
+ content = cls.export(document, options)
109
+ _write(path, content)
110
+
111
+ @classmethod
112
+ def store_graph(
113
+ cls,
114
+ document: Document,
115
+ path: Path
116
+ ) -> None:
117
+ """
118
+
119
+ Args:
120
+ document:
121
+ path:
122
+
123
+ Returns:
124
+ """
125
+ graph_exporter = GraphvizExporter()
126
+ graph_exporter.export_to_dot(document.tree, path)
127
+
128
+ @classmethod
129
+ def get_spine_types(
130
+ cls,
131
+ document: Document,
132
+ spine_types: Optional[Sequence[str]] = None
133
+ ) -> List[str]:
134
+ """
135
+
136
+ Args:
137
+ document:
138
+ spine_types:
139
+
140
+ Returns:
141
+
142
+ """
143
+ exporter = Exporter()
144
+ return exporter.get_spine_types(document, spine_types)
145
+
146
+ @classmethod
147
+ def merge(
148
+ cls,
149
+ contents: Sequence[str],
150
+ strict: Optional[bool] = False
151
+ ) -> Tuple[Document, List[Tuple[int, int]]]:
152
+ """
153
+
154
+ Args:
155
+ contents:
156
+ strict:
157
+
158
+ Returns:
159
+
160
+ """
161
+ if len(contents) < 2:
162
+ raise ValueError(f"Concatenation action requires at least two documents to concatenate."
163
+ f"But {len(contents)} was given.")
164
+
165
+ raise NotImplementedError("The merge function is not implemented yet.")
166
+
167
+ doc_a, err_a = cls.create(contents[0], strict=strict)
168
+ for i, content in enumerate(contents[1:]):
169
+ doc_b, err_b = cls.create(content, strict=strict)
170
+
171
+ if strict and (len(err_a) > 0 or len(err_b) > 0):
172
+ raise Exception(f"Errors were found during the creation of the documents "
173
+ f"while using the strict=True option. "
174
+ f"Description: concatenating: {err_a if len(err_a) > 0 else err_b}")
175
+
176
+ doc_a.add(doc_b)
177
+ return cls.export(
178
+ document=doc_a,
179
+ options=options
180
+ )
181
+
182
+ @classmethod
183
+ def concat(
184
+ cls,
185
+ contents: Sequence[str],
186
+ separator: Optional[str] = None
187
+ ) -> Tuple[Document, List[Tuple[int, int]]]:
188
+ """
189
+
190
+ Args:
191
+ contents:
192
+ separator:
193
+
194
+ Returns:
195
+
196
+ """
197
+ # Raw kern content
198
+ if separator is None:
199
+ separator = '\n'
200
+
201
+ if len(contents) == 0:
202
+ raise ValueError("No contents to merge. At least one content is required.")
203
+
204
+ raw_kern = ''
205
+ document = None
206
+ indexes = []
207
+ low_index = 0
208
+ high_index = 0
209
+
210
+ # Merge all fragments
211
+ for content in contents:
212
+ raw_kern += separator + content
213
+ document, _ = create(raw_kern)
214
+ high_index = document.measures_count()
215
+ indexes.append((low_index, high_index))
216
+
217
+ low_index = high_index + 1 # Next fragment start is the previous fragment end + 1
218
+
219
+ if document is None:
220
+ raise Exception("Failed to merge the contents. The document is None.")
221
+
222
+ return document, indexes
223
+
224
+ @classmethod
225
+ def parse_options_to_ExportOptions(
226
+ cls,
227
+ **kwargs: Any
228
+ ) -> ExportOptions:
229
+ """
230
+
231
+ Args:
232
+ **kwargs:
233
+
234
+ Returns:
235
+
236
+ """
237
+ options = ExportOptions.default()
238
+
239
+ # Compute the valid token categories
240
+ options.token_categories = TokenCategoryHierarchyMapper.valid(
241
+ include=kwargs.get('include', None),
242
+ exclude=kwargs.get('exclude', None)
243
+ )
244
+
245
+ # Use kwargs to update the ExportOptions object
246
+ for key, value in kwargs.items():
247
+ if key in ['include', 'exclude', 'token_categories']: # Skip these keys: generated manually
248
+ continue
249
+
250
+ if value is not None:
251
+ setattr(options, key, value)
252
+
253
+ return options
254
+
255
+
256
+ @deprecated("Use 'load' instead.")
257
+ def read(
258
+ path: Union[str, Path],
259
+ strict: Optional[bool] = False
260
+ ) -> (Document, List[str]):
261
+ """
262
+ Read a Humdrum **kern file.
263
+
264
+ Args:
265
+ path (Union[str, Path]): File path to read
266
+ strict (Optional[bool]): If True, raise an error if the **kern file has any errors. Otherwise, return a list of errors.
267
+
268
+ Returns (Document, List[str]): Document object and list of error messages. Empty list if no errors.
269
+
270
+ Examples:
271
+ >>> import kernpy as kp
272
+ >>> document, _ = kp.read('path/to/file.krn')
273
+
274
+ >>> document, errors = kp.read('path/to/file.krn')
275
+ >>> if len(errors) > 0:
276
+ >>> print(errors)
277
+ ['Error: Invalid **kern spine: 1', 'Error: Invalid **kern spine: 2']
278
+ """
279
+ return Generic.read(
280
+ path=Path(path),
281
+ strict=strict
282
+ )
283
+
284
+
285
+ @deprecated("Use 'loads' instead.")
286
+ def create(
287
+ content: str,
288
+ strict=False
289
+ ) -> (Document, []):
290
+ """
291
+ Create a Document object from a string encoded in Humdrum **kern format.
292
+
293
+ Args:
294
+ content: String encoded in Humdrum **kern format
295
+ strict: If True, raise an error if the **kern file has any errors. Otherwise, return a list of errors.
296
+
297
+ Returns (Document, list): Document object and list of error messages. Empty list if no errors.
298
+
299
+ Examples:
300
+ >>> import kernpy as kp
301
+ >>> document, errors = kp.create('**kern\n4e\n4f\n4g\n*-\n')
302
+ >>> if len(errors) > 0:
303
+ >>> print(errors)
304
+ ['Error: Invalid **kern spine: 1', 'Error: Invalid **kern spine: 2']
305
+ """
306
+ return Generic.create(
307
+ content=content,
308
+ strict=strict
309
+ )
310
+
311
+
312
+ @deprecated("Use 'dumps' instead.")
313
+ def export(
314
+ document: Document,
315
+ options: ExportOptions
316
+ ) -> str:
317
+ """
318
+ Export a Document object to a string.
319
+
320
+ Args:
321
+ document: Document object to export
322
+ options: Export options
323
+
324
+ Returns: Exported string
325
+
326
+ Examples:
327
+ >>> import kernpy as kp
328
+ >>> document, errors = kp.read('path/to/file.krn')
329
+ >>> options = kp.ExportOptions()
330
+ >>> content = kp.export(document, options)
331
+ """
332
+ return Generic.export(
333
+ document=document,
334
+ options=options
335
+ )
336
+
337
+
338
+ @deprecated("Use 'dump' instead.")
339
+ def store(
340
+ document: Document,
341
+ path: Union[str, Path],
342
+ options: ExportOptions
343
+ ) -> None:
344
+ """
345
+ Store a Document object to a file.
346
+
347
+ Args:
348
+ document (Document): Document object to store
349
+ path (Union[str, Path]): File path to store
350
+ options (ExportOptions): Export options
351
+
352
+ Returns: None
353
+
354
+ Examples:
355
+ >>> import kernpy as kp
356
+ >>> document, errors = kp.read('path/to/file.krn')
357
+ >>> options = kp.ExportOptions()
358
+ >>> kp.store(document, 'path/to/store.krn', options)
359
+
360
+ """
361
+ Generic.store(
362
+ document=document,
363
+ path=Path(path),
364
+ options=options
365
+ )
366
+
367
+
368
+ @deprecated("Use 'graph' instead.")
369
+ def store_graph(
370
+ document: Document,
371
+ path: Union[str, Path]
372
+ ) -> None:
373
+ """
374
+ Create a graph representation of a Document object using Graphviz. Save the graph to a file.
375
+
376
+ Args:
377
+ document (Document): Document object to create graph from
378
+ path (str): File path to save the graph
379
+
380
+ Returns (None): None
381
+
382
+ Examples:
383
+ >>> import kernpy as kp
384
+ >>> document, errors = kp.read('path/to/file.krn')
385
+ >>> kp.store_graph(document, 'path/to/graph.dot')
386
+ """
387
+ return Generic.store_graph(
388
+ document=document,
389
+ path=Path(path)
390
+ )
391
+
392
+
393
+ @deprecated("Use 'spine_types' instead.")
394
+ def get_spine_types(
395
+ document: Document,
396
+ spine_types: Optional[Sequence[str]] = None
397
+ ) -> List[str]:
398
+ """
399
+ Get the spines of a Document object.
400
+
401
+ Args:
402
+ document (Document): Document object to get spines from
403
+ spine_types (Optional[Sequence[str]]): List of spine types to get. If None, all spines are returned.
404
+
405
+ Returns (List[str]): List of spines
406
+
407
+ Examples:
408
+ >>> import kernpy as kp
409
+ >>> document, _ = kp.read('path/to/file.krn')
410
+ >>> kp.get_spine_types(document)
411
+ ['**kern', '**kern', '**kern', '**kern', '**root', '**harm']
412
+ >>> kp.get_spine_types(document, None)
413
+ ['**kern', '**kern', '**kern', '**kern', '**root', '**harm']
414
+ >>> kp.get_spine_types(document, ['**kern'])
415
+ ['**kern', '**kern', '**kern', '**kern']
416
+ >>> kp.get_spine_types(document, ['**kern', '**root'])
417
+ ['**kern', '**kern', '**kern', '**kern', '**root']
418
+ >>> kp.get_spine_types(document, ['**kern', '**root', '**harm'])
419
+ ['**kern', '**kern', '**kern', '**kern', '**root', '**harm']
420
+ >>> kp.get_spine_types(document, [])
421
+ []
422
+ """
423
+ return Generic.get_spine_types(
424
+ document=document,
425
+ spine_types=spine_types
426
+ )