File: unitatom.py

package info (click to toggle)
convertall 0.8.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,820 kB
  • sloc: python: 2,952; makefile: 7
file content (142 lines) | stat: -rw-r--r-- 5,137 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

#****************************************************************************
# unitatom.py, provides class to hold data on each available unit
#
# ConvertAll, a units conversion program
# Copyright (C) 2017, Douglas W. Bell
#
# This is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License, either Version 2 or any later
# version.  This program is distributed in the hope that it will be useful,
# but WITTHOUT ANY WARRANTY.  See the included LICENSE file for details.
#*****************************************************************************

import re
import copy
import unitdata


class UnitDatum:
    """Reads and stores data for a single unit, without an exponent.
    """
    badOpRegEx = re.compile(r'[^\d\.eE\+\-\*/]')
    eqnRegEx = re.compile(r'\[(.*?)\](.*)')
    def __init__(self, dataStr):
        """Initialize with a string from the data file.
        """
        dataList = dataStr.split('#')
        unitList = dataList.pop(0).split('=', 1)
        self.name = unitList.pop(0).strip()
        self.equiv = ''
        self.factor = 1.0
        self.fromEqn = ''   # used only for non-linear units
        self.toEqn = ''     # used only for non-linear units
        if unitList:
            self.equiv = unitList[0].strip()
            if self.equiv[0] == '[':   # used only for non-linear units
                try:
                    self.equiv, self.fromEqn = (UnitDatum.eqnRegEx.
                                                match(self.equiv).groups())
                    if ';' in self.fromEqn:
                        self.fromEqn, self.toEqn = self.fromEqn.split(';', 1)
                        self.toEqn = self.toEqn.strip()
                    self.fromEqn = self.fromEqn.strip()
                except AttributeError:
                    raise unitdata.UnitDataError(_('Bad equation for "{0}"').
                                                 format(self.name))
            else:                # split factor and equiv unit for linear
                parts = self.equiv.split(None, 1)
                if (len(parts) > 1 and
                    UnitDatum.badOpRegEx.search(parts[0]) == None):
                                      # only allowed digits and operators
                    try:
                        self.factor = float(eval(parts[0]))
                        self.equiv = parts[1]
                    except:
                        pass
            self.comments = [comm.strip() for comm in dataList]
            self.comments.extend([''] * (2 - len(self.comments)))
            self.keyWords = self.name.lower().split()
        self.viewLink = None
        self.typeName = ''

    def description(self):
        """Return name and 1st comment (usu. full name) if applicable.
        """
        if self.comments[0]:
            return '{0}  ({1})'.format(self.name, self.comments[0])
        return self.name

    def columnText(self, colNum):
        """Return text for given column number in the list view.
        """
        if colNum == 0:
            return self.description()
        if colNum == 1:
            return self.typeName
        return self.comments[1]

    def partialMatch(self, wordList):
        """Return True if parts of name start with items from wordList.
        """
        for word in wordList:
            for key in self.keyWords:
                if key.startswith(word):
                    return True
        return False

    def __lt__(self, other):
        """Less than comparison for sorting.
        """
        return self.name.lower() < other.name.lower()

    def __eq__(self, other):
        """Equality test.
        """
        return self.name.lower() == other.name.lower()


class UnitAtom:
    """Stores a unit datum or a temporary name with an exponent.
    """
    invalidExp = 1000
    def __init__(self, name='', unitDatum = None):
        """Initialize with either a text name or a unitDatum.
        """
        self.datum = None
        self.unitName = name
        self.exp = 1
        self.partialExp = ''  # starts with '^' for incomplete exp
        if unitDatum:
            self.datum = unitDatum
            self.unitName = unitDatum.name

    def unitValid(self):
        """Return True if unit and exponent are valid.
        """
        if (self.datum and self.datum.equiv and
            abs(self.exp) < UnitAtom.invalidExp):
            return True
        return False

    def unitText(self, absExp=False):
        """Return text for unit name with exponent or absolute value of exp.
        """
        exp = self.exp
        if absExp:
            exp = abs(self.exp)
        if self.partialExp:
            return '{0}{1}'.format(self.unitName, self.partialExp)
        if exp == 1:
            return self.unitName
        return '{0}^{1}'.format(self.unitName, exp)

    def __lt__(self, other):
        """Less than comparison for sorting.
        """
        return self.unitName.lower() < other.unitName.lower()

    def __eq__(self, other):
        """Equality test.
        """
        return self.unitName.lower() == other.unitName.lower()