• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nielstron / quantulum3 / 946

pending completion
946

cron

travis-ci-com

nielstron
Merge branch 'dev'

467 of 467 new or added lines in 14 files covered. (100.0%)

1812 of 1847 relevant lines covered (98.11%)

4.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.24
/quantulum3/load.py
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
"""
5✔
4
:mod:`Quantulum` unit and entity loading functions.
5
"""
6

7
import json
5✔
8
from collections import defaultdict
5✔
9
from pathlib import Path
5✔
10
from typing import Any, List, Tuple, Union
5✔
11

12
from . import classes as c
5✔
13
from . import language
5✔
14

15
TOPDIR = Path(__file__).parent or Path(".")
5✔
16

17
###############################################################################
18
_CACHE_DICT = defaultdict(dict)
5✔
19

20

21
def clear_caches():
5✔
22
    clear_units_cache()
5✔
23
    clear_entities_cache()
5✔
24
    clear_cache()
5✔
25

26

27
def reset_quantities():
5✔
28
    global USE_GENERAL_UNITS, USE_GENERAL_ENTITIES, USE_LANGUAGE_UNITS, USE_LANGUAGE_ENTITIES, USE_CUSTOM_UNITS, USE_CUSTOM_ENTITIES, USE_ADDITIONAL_UNITS, USE_ADDITIONAL_ENTITIES
29

30
    USE_GENERAL_UNITS = True
5✔
31
    USE_GENERAL_ENTITIES = True
5✔
32
    USE_LANGUAGE_UNITS = True
5✔
33
    USE_LANGUAGE_ENTITIES = True
5✔
34
    USE_CUSTOM_UNITS = False
5✔
35
    USE_CUSTOM_ENTITIES = False
5✔
36
    USE_ADDITIONAL_UNITS = True
5✔
37
    USE_ADDITIONAL_ENTITIES = True
5✔
38

39

40
def clear_cache():
5✔
41
    """
42
    Useful for testing.
43
    """
44
    _CACHE_DICT.clear()
5✔
45

46

47
class CustomQuantities:
5✔
48
    def __init__(
5✔
49
        self,
50
        custom_units: List[Union[str, Path]] = None,
51
        custom_entities: List[Union[str, Path]] = None,
52
        use_general_units: bool = False,
53
        use_language_units: bool = False,
54
        use_additional_units: bool = True,
55
        use_general_entities: bool = False,
56
        use_language_entities: bool = False,
57
        use_additional_entities: bool = True,
58
    ):
59
        """
60
        Load custom unites and entities into Quantulum via a context manager.
61

62
        :param custom_units: list of paths to custom units
63
        :param custom_entities: list of paths to custom entities
64
        :param use_general_units: Whether to also load the general units, by default False
65
        :param use_language_units: Whether to also load the language specific units, by default False
66
        :param use_additional_units: Whether to also load the additional units, by default True
67
        """
68
        self.custom_units = custom_units
5✔
69
        self.custom_entities = custom_entities
5✔
70
        self.use_general_units = use_general_units
5✔
71
        self.use_language_units = use_language_units
5✔
72
        self.use_additional_units = use_additional_units
5✔
73
        self.use_general_entities = use_general_entities
5✔
74
        self.use_language_entities = use_language_entities
5✔
75
        self.use_additional_entities = use_additional_entities
5✔
76

77
    def __enter__(self):
5✔
78
        self._record_current_state()
5✔
79

80
        if self.custom_units:
5✔
81
            load_custom_units(
5✔
82
                self.custom_units,
83
                use_general_units=self.use_general_units,
84
                use_language_units=self.use_language_units,
85
                use_additional_units=self.use_additional_units,
86
            )
87

88
        if self.custom_entities:
5✔
89
            load_custom_entities(
5✔
90
                self.custom_entities,
91
                use_general_entities=self.use_general_entities,
92
                use_language_entities=self.use_language_entities,
93
                use_additional_entities=self.use_additional_entities,
94
            )
95

96
    def __exit__(self, exc_type, exc_value, exc_tb):
5✔
97
        # reset flags
98
        self._reset_state()
5✔
99

100
    def _record_current_state(self):
5✔
101
        self.previous_general_units = USE_GENERAL_UNITS
5✔
102
        self.previous_general_entities = USE_GENERAL_ENTITIES
5✔
103
        self.previous_language_units = USE_LANGUAGE_UNITS
5✔
104
        self.previous_language_entities = USE_LANGUAGE_ENTITIES
5✔
105
        self.previous_custom_units = USE_CUSTOM_UNITS
5✔
106
        self.previous_custom_entities = USE_CUSTOM_ENTITIES
5✔
107
        self.previous_additional_units = USE_ADDITIONAL_UNITS
5✔
108
        self.previous_additional_entities = USE_ADDITIONAL_ENTITIES
5✔
109

110
    def _reset_state(self):
5✔
111
        global USE_GENERAL_UNITS, USE_GENERAL_ENTITIES, USE_LANGUAGE_UNITS, USE_LANGUAGE_ENTITIES, USE_CUSTOM_UNITS, USE_CUSTOM_ENTITIES, USE_ADDITIONAL_UNITS, USE_ADDITIONAL_ENTITIES
112

113
        USE_GENERAL_UNITS = self.previous_general_units
5✔
114
        USE_GENERAL_ENTITIES = self.previous_general_entities
5✔
115
        USE_LANGUAGE_UNITS = self.previous_language_units
5✔
116
        USE_LANGUAGE_ENTITIES = self.previous_language_entities
5✔
117
        USE_CUSTOM_UNITS = self.previous_custom_units
5✔
118
        USE_CUSTOM_ENTITIES = self.previous_custom_entities
5✔
119
        USE_ADDITIONAL_UNITS = self.previous_additional_units
5✔
120
        USE_ADDITIONAL_ENTITIES = self.previous_additional_entities
5✔
121

122

123
def cached(funct):
5✔
124
    """
125
    Decorator for caching language specific data
126
    :param funct: the method, dynamically responding to language. Only
127
                  parameter is lang
128
    :return: the method, dynamically responding to language but also caching
129
             results
130
    """
131
    assert callable(funct)
5✔
132

133
    def cached_function(*args, **kwargs):
5✔
134
        # create a hash of args and kwargs
135

136
        args_hash = hash((args, frozenset(kwargs.items())))
5✔
137

138
        try:
5✔
139
            return _CACHE_DICT[id(funct)][args_hash]
5✔
140
        except KeyError:
5✔
141
            result = funct(*args, **kwargs)
5✔
142
            _CACHE_DICT[id(funct)].update({args_hash: result})
5✔
143
            return result
5✔
144

145
    return cached_function
5✔
146

147

148
def object_pairs_hook_defer_duplicate_keys(object_pairs: List[Tuple[str, Any]]):
5✔
149
    keys = [x[0] for x in object_pairs]
5✔
150
    try:
5✔
151
        assert len(set(keys)) == len(keys)
5✔
152
    except AssertionError:
×
153
        raise AssertionError(
×
154
            "Dictionary has entries with same name: {}".format(
155
                [object_pairs[i] for i, k in enumerate(keys) if keys.count(k) > 1]
156
            )
157
        )
158
    return dict(object_pairs)
5✔
159

160

161
###############################################################################
162
@cached
5✔
163
def _get_load(lang="en_US"):
5✔
164
    return language.get("load", lang)
5✔
165

166

167
GENERAL_UNITS_PATH = TOPDIR.joinpath("units.json")
5✔
168
USE_GENERAL_UNITS = True
5✔
169
GENERAL_ENTITIES_PATH = TOPDIR.joinpath("entities.json")
5✔
170
USE_GENERAL_ENTITIES = True
5✔
171

172

173
def LANGUAGE_ENTITIES_PATH(lang="en_US"):
5✔
174
    return TOPDIR.joinpath(language.topdir(lang), "entities.json")
5✔
175

176

177
def LANGUAGE_UNITS_PATH(lang="en_US"):
5✔
178
    return TOPDIR.joinpath(language.topdir(lang), "units.json")
5✔
179

180

181
USE_LANGUAGE_UNITS = True
5✔
182
USE_LANGUAGE_ENTITIES = True
5✔
183

184

185
def _load_json(path_or_string: Union[Path, str]):
5✔
186
    if isinstance(path_or_string, Path):
5✔
187
        with path_or_string.open("r", encoding="utf-8") as jsonfile:
5✔
188
            return jsonfile.read()
5✔
189
    elif isinstance(path_or_string, str) and path_or_string.endswith(".json"):
×
190
        with open(path_or_string, "r", encoding="utf-8") as jsonfile:
×
191
            return jsonfile.read()
×
192
    return path_or_string
×
193

194

195
def _load_json_dict(path_or_string: Union[Path, str, dict]):
5✔
196
    if isinstance(path_or_string, dict):
5✔
197
        return path_or_string
5✔
198
    return json.loads(
5✔
199
        _load_json(path_or_string),
200
        object_pairs_hook=object_pairs_hook_defer_duplicate_keys,
201
    )
202

203

204
CUSTOM_ENTITIES = defaultdict(dict)
5✔
205
USE_CUSTOM_ENTITIES = True
5✔
206

207
ADDITIONAL_ENTITIES = defaultdict(dict)
5✔
208
USE_ADDITIONAL_ENTITIES = True
5✔
209

210
CUSTOM_UNITS = defaultdict(dict)
5✔
211
USE_CUSTOM_UNITS = True
5✔
212

213
ADDITIONAL_UNITS = defaultdict(dict)
5✔
214
USE_ADDITIONAL_UNITS = True
5✔
215

216

217
###############################################################################
218
def to_int_iff_int(value):
5✔
219
    """
220
    Returns int type number if the value is an integer value
221
    :param value:
222
    :return:
223
    """
224
    try:
5✔
225
        if int(value) == value:
5✔
226
            return int(value)
5✔
227
    except (TypeError, ValueError):
5✔
228
        pass
5✔
229
    return value
5✔
230

231

232
def pluralize(singular, count=None, lang="en_US"):
5✔
233
    # Make spelling integers more natural
234
    count = to_int_iff_int(count)
5✔
235
    return _get_load(lang).pluralize(singular, count)
5✔
236

237

238
def number_to_words(count, lang="en_US"):
5✔
239
    # Make spelling integers more natural
240
    count = to_int_iff_int(count)
5✔
241
    return _get_load(lang).number_to_words(count)
5✔
242

243

244
###############################################################################
245
METRIC_PREFIXES = {
5✔
246
    "Y": "yotta",
247
    "Z": "zetta",
248
    "E": "exa",
249
    "P": "peta",
250
    "T": "tera",
251
    "G": "giga",
252
    "M": "mega",
253
    "k": "kilo",
254
    "h": "hecto",
255
    "da": "deca",
256
    "d": "deci",
257
    "c": "centi",
258
    "m": "milli",
259
    "µ": "micro",
260
    "n": "nano",
261
    "p": "pico",
262
    "f": "femto",
263
    "a": "atto",
264
    "z": "zepto",
265
    "y": "yocto",
266
    "Ki": "kibi",
267
    "Mi": "mebi",
268
    "Gi": "gibi",
269
    "Ti": "tebi",
270
    "Pi": "pebi",
271
    "Ei": "exbi",
272
    "Zi": "zebi",
273
    "Yi": "yobi",
274
}
275

276

277
###############################################################################
278
def get_key_from_dimensions(derived):
5✔
279
    """
280
    Translate dimensionality into key for DERIVED_UNI and DERIVED_ENT dicts.
281
    """
282

283
    return tuple((i["base"], i["power"]) for i in derived)
5✔
284

285

286
###############################################################################
287
class Entities(object):
5✔
288
    def __init__(self, entity_dicts: List[Union[Path, str, dict]]):
5✔
289
        """
290
        Load entities from JSON file.
291
        """
292

293
        # Merge entity dictionarys
294
        all_entities = defaultdict(dict)
5✔
295
        for ed in entity_dicts:
5✔
296
            for new_name, new_ent in _load_json_dict(ed).items():
5✔
297
                all_entities[new_name].update(new_ent)
5✔
298

299
        self.names = dict(
5✔
300
            (
301
                name,
302
                c.Entity(
303
                    name=name,
304
                    dimensions=props.get("dimensions", []),
305
                    uri=props.get("URI"),
306
                ),
307
            )
308
            for name, props in all_entities.items()
309
        )
310

311
        # Generate derived units
312
        derived_ent = defaultdict(set)
5✔
313
        for entity in self.names.values():
5✔
314
            if not entity.dimensions:
5✔
315
                continue
5✔
316
            perms = self.get_dimension_permutations(entity.dimensions)
5✔
317
            for perm in perms:
5✔
318
                key = get_key_from_dimensions(perm)
5✔
319
                derived_ent[key].add(entity)
5✔
320

321
        self.derived = derived_ent
5✔
322

323
    def __getitem__(self, item):
5✔
324
        return self.names[item]
×
325

326
    def get_dimension_permutations(self, derived):
5✔
327
        """
328
        Get all possible dimensional definitions for an entity.
329
        """
330

331
        new_derived = defaultdict(int)
5✔
332
        for item in derived:
5✔
333
            new = self.names[item["base"]].dimensions
5✔
334
            if new:
5✔
335
                for new_item in new:
5✔
336
                    new_derived[new_item["base"]] += new_item["power"] * item["power"]
5✔
337
            else:
338
                new_derived[item["base"]] += item["power"]
5✔
339

340
        final = [
5✔
341
            [{"base": i[0], "power": i[1]} for i in list(new_derived.items())],
342
            derived,
343
        ]
344
        final = [sorted(i, key=lambda x: x["base"]) for i in final]
5✔
345

346
        candidates = []
5✔
347
        for item in final:
5✔
348
            if item not in candidates:
5✔
349
                candidates.append(item)
5✔
350

351
        return candidates
5✔
352

353

354
_CACHED_ENTITIES = {}
5✔
355

356

357
def clear_entities_cache():
5✔
358
    _CACHED_ENTITIES.clear()
5✔
359

360

361
def entities(
5✔
362
    lang: str = "en_US",
363
) -> Entities:
364
    """
365
    Cached entity object
366

367
    Parameters
368
    ----------
369
    lang : str
370
        Language code, loads the default and language specific entities.
371

372
    Returns
373
    -------
374
    Entities
375
        Entities object
376
    """
377

378
    args_hash = hash(
5✔
379
        (
380
            lang,
381
            USE_GENERAL_ENTITIES,
382
            USE_LANGUAGE_ENTITIES,
383
            USE_ADDITIONAL_ENTITIES,
384
            USE_CUSTOM_ENTITIES,
385
        )
386
    )
387
    if args_hash not in _CACHED_ENTITIES:
5✔
388
        entities_list = []
5✔
389
        if USE_GENERAL_ENTITIES:
5✔
390
            entities_list.append(GENERAL_ENTITIES_PATH)
5✔
391
        if USE_LANGUAGE_ENTITIES:
5✔
392
            entities_list.append(LANGUAGE_ENTITIES_PATH(lang))
5✔
393
        if USE_ADDITIONAL_ENTITIES:
5✔
394
            entities_list.append(ADDITIONAL_ENTITIES)
5✔
395
        if USE_CUSTOM_ENTITIES:
5✔
396
            entities_list.extend(CUSTOM_ENTITIES)
5✔
397

398
        _CACHED_ENTITIES[args_hash] = Entities(entities_list)
5✔
399

400
    return _CACHED_ENTITIES[args_hash]
5✔
401

402

403
###############################################################################
404
def get_derived_units(names):
5✔
405
    """
406
    Create dictionary of unit dimensions.
407
    """
408

409
    derived_uni = {}
5✔
410

411
    for name in names:
5✔
412
        key = get_key_from_dimensions(names[name].dimensions)
5✔
413
        derived_uni[key] = names[name]
5✔
414
        plain_derived = [{"base": name, "power": 1}]
5✔
415
        key = get_key_from_dimensions(plain_derived)
5✔
416
        derived_uni[key] = names[name]
5✔
417
        if not names[name].dimensions:
5✔
418
            names[name].dimensions = plain_derived
5✔
419
        names[name].dimensions = [
5✔
420
            {"base": names[i["base"]].name, "power": i["power"]}
421
            for i in names[name].dimensions
422
        ]
423

424
    return derived_uni
5✔
425

426

427
###############################################################################
428
class Units(object):
5✔
429
    def __init__(self, unit_dict_json: List[Union[str, Path, dict]], lang="en_US"):
5✔
430
        """
431
        Load units from JSON file.
432

433
        Parameters
434
        ----------
435
        unit_paths : List[Union[str, Path, dict]]
436
            Paths to unit JSON files.
437
        lang : str
438
            Language code, loads the default and language specific units.
439
        custom_entities : List[Union[Path, str, dict]]
440
            Paths to custom entity JSON files. If a list is provided, no default language
441
            or general entities will be loaded.
442
        """
443

444
        # names of all units
445
        self.names = {}
5✔
446
        self.symbols, self.symbols_lower = defaultdict(set), defaultdict(set)
5✔
447
        self.surfaces, self.surfaces_lower = defaultdict(set), defaultdict(set)
5✔
448
        self.prefix_symbols = defaultdict(set)
5✔
449
        self.lang = lang
5✔
450

451
        unit_dict = defaultdict(dict)
5✔
452
        for ud in unit_dict_json:
5✔
453
            for name, unit in _load_json_dict(ud).items():
5✔
454
                for nname, nunit in self.prefixed_units(name, unit):
5✔
455
                    unit_dict[nname].update(nunit)
5✔
456

457
        for name, unit in unit_dict.items():
5✔
458
            self.load_unit(name, unit)
5✔
459

460
        self.derived = get_derived_units(self.names)
5✔
461

462
        # symbols of all units
463
        self.symbols_all = self.symbols.copy()
5✔
464
        self.symbols_all.update(self.symbols_lower)
5✔
465

466
        # surfaces of all units
467
        self.surfaces_all = self.surfaces.copy()
5✔
468
        self.surfaces_all.update(self.surfaces_lower)
5✔
469

470
    def __getitem__(self, name):
5✔
471
        return self.names[name]
×
472

473
    def load_unit(self, name, unit):
5✔
474
        try:
5✔
475
            assert name not in self.names
5✔
476
        except AssertionError:  # pragma: no cover
477
            msg = "Two units with same name in units.json: %s" % name
478
            raise Exception(msg)
479

480
        obj = c.Unit(
5✔
481
            name=name,
482
            surfaces=unit.get("surfaces", []),
483
            entity=entities(self.lang).names[unit["entity"]],
484
            uri=unit.get("URI"),
485
            symbols=unit.get("symbols", []),
486
            dimensions=unit.get("dimensions", []),
487
            currency_code=unit.get("currency_code"),
488
            lang=self.lang,
489
        )
490

491
        self.names[name] = obj
5✔
492

493
        for symbol in unit.get("symbols", []):
5✔
494
            self.symbols[symbol].add(obj)
5✔
495
            self.symbols_lower[symbol.lower()].add(obj)
5✔
496
            if unit["entity"] == "currency":
5✔
497
                self.prefix_symbols[symbol].add(obj)
5✔
498

499
        for surface in unit.get("surfaces", []):
5✔
500
            self.surfaces[surface].add(obj)
5✔
501
            self.surfaces_lower[surface.lower()].add(obj)
5✔
502
            plural = pluralize(surface, lang=self.lang)
5✔
503
            self.surfaces[plural].add(obj)
5✔
504
            self.surfaces_lower[plural.lower()].add(obj)
5✔
505

506
    @staticmethod
5✔
507
    def prefixed_units(name, unit):
3✔
508
        yield name, unit
5✔
509
        # If SI-prefixes are given, add them
510
        for prefix in unit.get("prefixes", []):
5✔
511
            assert (
5✔
512
                prefix in METRIC_PREFIXES
513
            ), "Given prefix '{}' for unit '{}' not supported".format(prefix, name)
514
            assert (
5✔
515
                len(unit["dimensions"]) <= 1
516
            ), "Prefixing not supported for multiple dimensions in {}".format(name)
517

518
            uri = METRIC_PREFIXES[prefix].capitalize() + unit["URI"].lower()
5✔
519
            # we usually do not want the "_(unit)" postfix for prefixed units
520
            uri = uri.replace("_(unit)", "")
5✔
521

522
            yield METRIC_PREFIXES[prefix] + name, {
5✔
523
                "surfaces": [METRIC_PREFIXES[prefix] + i for i in unit["surfaces"]],
524
                "entity": unit["entity"],
525
                "URI": uri,
526
                "dimensions": [],
527
                "symbols": [prefix + i for i in unit["symbols"]],
528
            }
529

530

531
_CACHED_UNITS = {}
5✔
532

533

534
def clear_units_cache():
5✔
535
    _CACHED_UNITS.clear()
5✔
536

537

538
def units(
5✔
539
    lang: str = "en_US",
540
) -> Units:
541
    """
542
    Cached unit object
543

544
    Parameters
545
    ----------
546
    lang : str
547
        Language code, loads the default and language specific units.
548

549
    Returns
550
    -------
551
    Units
552
        Units object
553
    """
554

555
    args_hash = hash(
5✔
556
        (
557
            lang,
558
            USE_GENERAL_UNITS,
559
            USE_LANGUAGE_UNITS,
560
            USE_ADDITIONAL_UNITS,
561
            USE_CUSTOM_UNITS,
562
        )
563
    )
564

565
    if args_hash not in _CACHED_UNITS:
5✔
566
        units_list = []
5✔
567
        if USE_GENERAL_UNITS:
5✔
568
            units_list.append(GENERAL_UNITS_PATH)
5✔
569
        if USE_LANGUAGE_UNITS:
5✔
570
            units_list.append(LANGUAGE_UNITS_PATH(lang))
5✔
571
        if USE_ADDITIONAL_UNITS:
5✔
572
            units_list.append(ADDITIONAL_UNITS)
5✔
573
        if USE_CUSTOM_UNITS:
5✔
574
            units_list.extend(CUSTOM_UNITS)
5✔
575

576
        _CACHED_UNITS[args_hash] = Units(units_list, lang)
5✔
577

578
    return _CACHED_UNITS[args_hash]
5✔
579

580

581
###############################################################################
582
@cached
5✔
583
def training_set(lang="en_US"):
5✔
584
    training_set_ = []
5✔
585

586
    path = language.topdir(lang).joinpath("train")
5✔
587
    for file in path.iterdir():
5✔
588
        if file.suffix == ".json":
5✔
589
            with file.open("r", encoding="utf-8") as train_file:
5✔
590
                training_set_ += json.load(train_file)
5✔
591

592
    return training_set_
5✔
593

594

595
###############################################################################
596
def add_custom_unit(name: str, **kwargs):
5✔
597
    """
598
    Adds a custom unit to the set of default units
599
    Note: causes a reload of all units
600
    :param name: Name of the unit to add, should preferably be unique,
601
    otherwise will overwrite attributes in existing units
602
    :param kwargs: properties of the unit as found in units.json, i.e. surfaces=["centimetre"]
603
    """
604
    ADDITIONAL_UNITS[name].update(kwargs)
5✔
605
    clear_caches()
5✔
606

607

608
def remove_custom_unit(name: str):
5✔
609
    """
610
    Removes a unit from the set of custom units
611
    Note: causes a reload of all units
612
    :param name: Name of the unit to remove. This will not affect units that are loaded per default.
613
    """
614
    ADDITIONAL_UNITS.pop(name)
5✔
615
    clear_caches()
5✔
616

617

618
def add_custom_entity(name: str, **kwargs):
5✔
619
    """
620
    Adds a custom entity to the set of default entities
621
    Note: causes a reload of all entities
622
    :param name: Name of the entity to add, should preferably be unique,
623
    otherwise will overwrite attributes in existing entities
624
    :param kwargs: properties of the entity as found in entities.json, i.e. surfaces=["centimetre"]
625
    """
626
    ADDITIONAL_ENTITIES[name].update(kwargs)
5✔
627
    clear_caches()
5✔
628

629

630
def remove_custom_entity(name: str):
5✔
631
    """
632
    Removes a entity from the set of custom entities
633
    Note: causes a reload of all entities
634
    :param name: Name of the entity to remove. This will not affect entities that are loaded per default.
635
    """
636
    ADDITIONAL_ENTITIES.pop(name)
5✔
637
    clear_caches()
5✔
638

639

640
def load_custom_units(
5✔
641
    unit_dict_json: List[Union[str, Path]],
642
    use_general_units: bool = False,
643
    use_language_units: bool = False,
644
    use_additional_units: bool = True,
645
):
646
    """
647
    Load custom units from a dictionary or json file.
648

649
    Parameters
650
    ----------
651
    unit_dict_json : List[Dict]
652
        A list of dictionaries or paths to json files containing the units to load.
653
    use_general_units : bool, optional
654
        Whether to also load the general units, by default False
655
    use_language_units : bool, optional
656
        Whether to also load the language specific units, by default False
657
    use_additional_units : bool, optional
658
        Whether to also load the additional units (from the add_custom_unit functions), by default True
659
    """
660

661
    if not isinstance(unit_dict_json, list):
5✔
662
        unit_dict_json = [unit_dict_json]
5✔
663

664
    global USE_GENERAL_UNITS, USE_LANGUAGE_UNITS, USE_ADDITIONAL_UNITS, USE_CUSTOM_UNITS, CUSTOM_UNITS
665

666
    USE_GENERAL_UNITS = use_general_units
5✔
667
    USE_LANGUAGE_UNITS = use_language_units
5✔
668
    USE_ADDITIONAL_UNITS = use_additional_units
5✔
669
    USE_CUSTOM_UNITS = True
5✔
670

671
    CUSTOM_UNITS = unit_dict_json
5✔
672

673
    clear_units_cache()
5✔
674

675

676
def load_custom_entities(
5✔
677
    entity_dict_json: List[Union[str, Path]],
678
    use_general_entities: bool = False,
679
    use_language_entities: bool = False,
680
    use_additional_entities: bool = True,
681
):
682
    """
683
    Load custom entities from a dictionary or json file.
684

685
    Parameters
686
    ----------
687
    entity_dict_json : List[Dict]
688
        A list of dictionaries or paths to json files containing the entities to load.
689
    use_general_entities : bool, optional
690
        Whether to also load the general entities, by default False
691
    use_language_entities : bool, optional
692
        Whether to also load the language specific entities, by default False
693
    use_additional_entities : bool, optional
694
        Whether to also load the additional entities (from the add_custom_entity functions), by default True
695
    """
696

697
    if not isinstance(entity_dict_json, list):
5✔
698
        entity_dict_json = [entity_dict_json]
5✔
699

700
    global USE_GENERAL_ENTITIES, USE_LANGUAGE_ENTITIES, USE_ADDITIONAL_ENTITIES, USE_CUSTOM_ENTITIES, CUSTOM_ENTITIES
701

702
    USE_GENERAL_ENTITIES = use_general_entities
5✔
703
    USE_LANGUAGE_ENTITIES = use_language_entities
5✔
704
    USE_ADDITIONAL_ENTITIES = use_additional_entities
5✔
705
    USE_CUSTOM_ENTITIES = True
5✔
706

707
    CUSTOM_ENTITIES = entity_dict_json
5✔
708

709
    clear_entities_cache()
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc