Ticket #2365: DecimalField-FloatField.2.diff
| File DecimalField-FloatField.2.diff, 143.4 kB (added by adurdin@gmail.com, 2 years ago) |
|---|
-
django/oldforms/__init__.py
old new 745 745 raise validators.CriticalValidationError, gettext("Enter a whole number between 0 and 32,767.") 746 746 747 747 class FloatField(TextField): 748 def __init__(self, field_name, is_required=False, validator_list=None): 749 if validator_list is None: validator_list = [] 750 validator_list = [validators.isValidFloat] + validator_list 751 TextField.__init__(self, field_name, is_required=is_required, validator_list=validator_list) 752 753 def html2python(data): 754 if data == '' or data is None: 755 return None 756 return float(data) 757 html2python = staticmethod(html2python) 758 759 class DecimalField(TextField): 748 760 def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=None): 749 761 if validator_list is None: validator_list = [] 750 762 self.max_digits, self.decimal_places = max_digits, decimal_places 751 validator_list = [self.isValidFloat] + validator_list 752 TextField.__init__(self, field_name, max_digits+2, max_digits+2, is_required, validator_list) 763 validator_list = [self.isValidDecimal] + validator_list 764 # Initialise the TextField, making sure it's large enough to fit the number with a - sign and a decimal point. 765 super(DecimalField, self).__init__(field_name, max_digits+2, max_digits+2, is_required, validator_list) 753 766 754 def isValid Float(self, field_data, all_data):755 v = validators.IsValid Float(self.max_digits, self.decimal_places)767 def isValidDecimal(self, field_data, all_data): 768 v = validators.IsValidDecimal(self.max_digits, self.decimal_places) 756 769 try: 757 770 v(field_data, all_data) 758 771 except validators.ValidationError, e: … … 761 774 def html2python(data): 762 775 if data == '' or data is None: 763 776 return None 764 return float(data) 777 try: 778 import decimal 779 except ImportError: 780 from django.utils import decimal 781 try: 782 return decimal.Decimal(data) 783 except decimal.InvalidOperation, e: 784 raise ValueError, e 765 785 html2python = staticmethod(html2python) 766 786 767 787 #################### -
django/db/models/fields/__init__.py
old new 668 668 def get_manipulator_field_objs(self): 669 669 return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)] 670 670 671 class FloatField(Field):671 class DecimalField(Field): 672 672 empty_strings_allowed = False 673 673 def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs): 674 674 self.max_digits, self.decimal_places = max_digits, decimal_places 675 675 Field.__init__(self, verbose_name, name, **kwargs) 676 676 677 def to_python(self, value): 678 try: 679 import decimal 680 except ImportError: 681 from django.utils import decimal 682 try: 683 return decimal.Decimal(value) 684 except (decimal.InvalidOperation): 685 raise validators.ValidationError, gettext("This value must be a decimal number.") 686 687 def _format(self, value): 688 if isinstance(value, basestring): 689 return value 690 else: 691 return self.format_number(value) 692 693 def format_number(self, value): 694 """Formats a number into a string with the requisite number of digits and decimal places.""" 695 696 num_chars = self.max_digits 697 # Allow for a decimal point 698 if self.decimal_places > 0: 699 num_chars += 1 700 # Allow for a minus sign 701 if value < 0: 702 num_chars += 1 703 704 return "%.*f" % (self.decimal_places, value) 705 706 def get_db_prep_save(self, value): 707 value = value # self._format(value) 708 return super(DecimalField, self).get_db_prep_save(value) 709 710 def get_db_prep_lookup(self, lookup_type, value): 711 if lookup_type == 'range': 712 value = [self._format(v) for v in value] 713 else: 714 value = self._format(value) 715 return super(DecimalField, self).get_db_prep_lookup(lookup_type, value) 716 677 717 def get_manipulator_field_objs(self): 678 return [curry(oldforms. FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]718 return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)] 679 719 720 def formfield(self, **kwargs): 721 "Returns a django.newforms.Field instance for this database Field." 722 defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 723 'max_digits': self.max_digits, 'decimal_places': self.decimal_places} 724 defaults.update(kwargs) 725 return forms.DecimalField(**defaults) 726 727 class FloatField(Field): 728 empty_strings_allowed = False 729 def __init__(self, verbose_name=None, name=None, **kwargs): 730 Field.__init__(self, verbose_name, name, **kwargs) 731 732 def get_manipulator_field_objs(self): 733 return [oldforms.FloatField] 734 735 def formfield(self, **kwargs): 736 "Returns a django.newforms.FloatField instance for this database Field." 737 defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name)} 738 defaults.update(kwargs) 739 return forms.FloatField(**defaults) 740 680 741 class ImageField(FileField): 681 742 def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): 682 743 self.width_field, self.height_field = width_field, height_field -
django/db/backends/ado_mssql/creation.py
old new 5 5 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 6 6 'DateField': 'smalldatetime', 7 7 'DateTimeField': 'smalldatetime', 8 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 8 9 'FileField': 'varchar(100)', 9 10 'FilePathField': 'varchar(100)', 10 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',11 'FloatField': 'double precision', 11 12 'ImageField': 'varchar(100)', 12 13 'IntegerField': 'int', 13 14 'IPAddressField': 'char(15)', -
django/db/backends/postgresql/introspection.py
old new 79 79 1114: 'DateTimeField', 80 80 1184: 'DateTimeField', 81 81 1266: 'TimeField', 82 1700: ' FloatField',82 1700: 'DecimalField', 83 83 } -
django/db/backends/postgresql/creation.py
old new 9 9 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 10 10 'DateField': 'date', 11 11 'DateTimeField': 'timestamp with time zone', 12 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 12 13 'FileField': 'varchar(100)', 13 14 'FilePathField': 'varchar(100)', 14 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',15 'FloatField': 'double precision', 15 16 'ImageField': 'varchar(100)', 16 17 'IntegerField': 'integer', 17 18 'IPAddressField': 'inet', -
django/db/backends/sqlite3/base.py
old new 17 17 module = 'sqlite3' 18 18 raise ImproperlyConfigured, "Error loading %s module: %s" % (module, e) 19 19 20 try: 21 # Only exists in Python 2.4+ 22 import decimal 23 except ImportError: 24 # Import copy of decimal.py from Python 2.4 25 from django.utils import decimal 26 20 27 DatabaseError = Database.DatabaseError 21 28 22 29 Database.register_converter("bool", lambda s: str(s) == '1') … … 25 32 Database.register_converter("datetime", util.typecast_timestamp) 26 33 Database.register_converter("timestamp", util.typecast_timestamp) 27 34 Database.register_converter("TIMESTAMP", util.typecast_timestamp) 35 Database.register_converter("decimal", util.typecast_decimal) 36 Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) 28 37 29 38 def utf8rowFactory(cursor, row): 30 39 def utf8(s): -
django/db/backends/sqlite3/creation.py
old new 8 8 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 9 9 'DateField': 'date', 10 10 'DateTimeField': 'datetime', 11 'DecimalField': 'decimal', 11 12 'FileField': 'varchar(100)', 12 13 'FilePathField': 'varchar(100)', 13 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',14 'FloatField': 'real', 14 15 'ImageField': 'varchar(100)', 15 16 'IntegerField': 'integer', 16 17 'IPAddressField': 'char(15)', -
django/db/backends/util.py
old new 1 1 import datetime 2 2 from time import time 3 3 4 try: 5 # Only exists in Python 2.4+ 6 import decimal 7 except ImportError: 8 # Import copy of decimal.py from Python 2.4 9 from django.utils import decimal 10 4 11 class CursorDebugWrapper(object): 5 12 def __init__(self, cursor, db): 6 13 self.cursor = cursor … … 85 92 if not s: return False 86 93 return str(s)[0].lower() == 't' 87 94 95 def typecast_decimal(s): 96 if s is None: return None 97 return decimal.Decimal(s) 98 88 99 ############################################### 89 100 # Converters from Python to database (string) # 90 101 ############################################### … … 92 103 def rev_typecast_boolean(obj, d): 93 104 return obj and '1' or '0' 94 105 106 def rev_typecast_decimal(d): 107 if d is None: return None 108 return str(d) 109 95 110 ################################################################################## 96 111 # Helper functions for dictfetch* for databases that don't natively support them # 97 112 ################################################################################## -
django/db/backends/mysql/base.py
old new 23 23 FIELD_TYPE.DATETIME: util.typecast_timestamp, 24 24 FIELD_TYPE.DATE: util.typecast_date, 25 25 FIELD_TYPE.TIME: util.typecast_time, 26 FIELD_TYPE.DECIMAL: util.typecast_decimal, 26 27 }) 27 28 28 29 # This should match the numerical portion of the version numbers (we can treat -
django/db/backends/mysql/introspection.py
old new 76 76 DATA_TYPES_REVERSE = { 77 77 FIELD_TYPE.BLOB: 'TextField', 78 78 FIELD_TYPE.CHAR: 'CharField', 79 FIELD_TYPE.DECIMAL: ' FloatField',79 FIELD_TYPE.DECIMAL: 'DecimalField', 80 80 FIELD_TYPE.DATE: 'DateField', 81 81 FIELD_TYPE.DATETIME: 'DateTimeField', 82 82 FIELD_TYPE.DOUBLE: 'FloatField', -
django/db/backends/mysql/creation.py
old new 9 9 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 10 10 'DateField': 'date', 11 11 'DateTimeField': 'datetime', 12 'DecimalField': 'decimal(%(max_digits)s, %(decimal_places)s)', 12 13 'FileField': 'varchar(100)', 13 14 'FilePathField': 'varchar(100)', 14 'FloatField': ' numeric(%(max_digits)s, %(decimal_places)s)',15 'FloatField': 'double precision', 15 16 'ImageField': 'varchar(100)', 16 17 'IntegerField': 'integer', 17 18 'IPAddressField': 'char(15)', -
django/db/backends/oracle/introspection.py
old new 46 46 1114: 'DateTimeField', 47 47 1184: 'DateTimeField', 48 48 1266: 'TimeField', 49 1700: ' FloatField',49 1700: 'DecimalField', 50 50 } -
django/db/backends/oracle/creation.py
old new 5 5 'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)', 6 6 'DateField': 'date', 7 7 'DateTimeField': 'date', 8 'DecimalField': 'number(%(max_digits)s, %(decimal_places)s)', 8 9 'FileField': 'varchar2(100)', 9 10 'FilePathField': 'varchar2(100)', 10 'FloatField': ' number(%(max_digits)s, %(decimal_places)s)',11 'FloatField': 'double precision', 11 12 'ImageField': 'varchar2(100)', 12 13 'IntegerField': 'integer', 13 14 'IPAddressField': 'char(15)', -
django/db/backends/postgresql_psycopg2/introspection.py
old new 79 79 1114: 'DateTimeField', 80 80 1184: 'DateTimeField', 81 81 1266: 'TimeField', 82 1700: ' FloatField',82 1700: 'DecimalField', 83 83 } -
django/core/validators.py
old new 25 25 r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom 26 26 r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string 27 27 r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain 28 decimal_re = re.compile(r'^-?(?P<digits>\d+)(\.(?P<decimals>\d+))?$') 28 29 integer_re = re.compile(r'^-?\d+$') 29 30 ip4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') 30 31 phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE) … … 403 404 if val != int(val): 404 405 raise ValidationError, gettext("This value must be a power of %s.") % self.power_of 405 406 406 class IsValid Float(object):407 class IsValidDecimal(object): 407 408 def __init__(self, max_digits, decimal_places): 408 409 self.max_digits, self.decimal_places = max_digits, decimal_places 409 410 410 411 def __call__(self, field_data, all_data): 411 data = str(field_data) 412 try: 413 float(data) 414 except ValueError: 412 match = decimal_re.search(str(field_data)) 413 if not match: 415 414 raise ValidationError, gettext("Please enter a valid decimal number.") 416 # Negative floats require more space to input. 417 max_allowed_length = data.startswith('-') and (self.max_digits + 2) or (self.max_digits + 1) 418 if len(data) > max_allowed_length: 415 416 digits = len(match.group('digits') or '') 417 decimals = len(match.group('decimals') or '') 418 419 if digits + decimals > self.max_digits: 419 420 raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.", 420 421 "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits 421 if (not '.' in data and len(data) > (max_allowed_length - self.decimal_places - 1)) or ('.' in data and len(data) > (max_allowed_length - (self.decimal_places - len(data.split('.')[1])))):422 if digits > (self.max_digits - self.decimal_places): 422 423 raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.", 423 424 "Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places) 424 if '.' in data and len(data.split('.')[1])> self.decimal_places:425 if decimals > self.decimal_places: 425 426 raise ValidationError, ngettext("Please enter a valid decimal number with at most %s decimal place.", 426 427 "Please enter a valid decimal number with at most %s decimal places.", self.decimal_places) % self.decimal_places 427 428 429 def isValidFloat(field_data, all_data): 430 data = str(field_data) 431 try: 432 float(data) 433 except ValueError: 434 raise ValidationError, gettext("Please enter a valid floating point number.") 435 428 436 class HasAllowableSize(object): 429 437 """ 430 438 Checks that the file-upload field data is a certain size. min_size and -
django/core/management.py
old new 804 804 if field_type == 'CharField' and row[3]: 805 805 extra_params['maxlength'] = row[3] 806 806 807 if field_type == ' FloatField':807 if field_type == 'DecimalField': 808 808 extra_params['max_digits'] = row[4] 809 809 extra_params['decimal_places'] = row[5] 810 810 … … 879 879 e.add(opts, '"%s": You can\'t use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.' % f.name) 880 880 if isinstance(f, models.CharField) and f.maxlength in (None, 0): 881 881 e.add(opts, '"%s": CharFields require a "maxlength" attribute.' % f.name) 882 if isinstance(f, models. FloatField):882 if isinstance(f, models.DecimalField): 883 883 if f.decimal_places is None: 884 e.add(opts, '"%s": FloatFields require a "decimal_places" attribute.' % f.name)884 e.add(opts, '"%s": DecimalFields require a "decimal_places" attribute.' % f.name) 885 885 if f.max_digits is None: 886 e.add(opts, '"%s": FloatFields require a "max_digits" attribute.' % f.name)886 e.add(opts, '"%s": DecimalFields require a "max_digits" attribute.' % f.name) 887 887 if isinstance(f, models.FileField) and not f.upload_to: 888 888 e.add(opts, '"%s": FileFields require an "upload_to" attribute.' % f.name) 889 889 if isinstance(f, models.ImageField): -
django/newforms/fields.py
old new 17 17 'RegexField', 'EmailField', 'URLField', 'BooleanField', 18 18 'ChoiceField', 'NullBooleanField', 'MultipleChoiceField', 19 19 'ComboField', 'MultiValueField', 20 'FloatField', 'DecimalField', 20 21 'SplitDateTimeField', 21 22 ) 22 23 … … 28 29 except NameError: 29 30 from sets import Set as set # Python 2.3 fallback 30 31 32 try: 33 from decimal import Decimal # Only available in Python 2.4+ 34 except ImportError: 35 from django.utils.decimal import Decimal # Use bundled version for Python 2.3 36 31 37 class Field(object): 32 38 widget = TextInput # Default widget to use when rendering this type of Field. 33 39 hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". … … 128 134 raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value) 129 135 return value 130 136 137 class FloatField(Field): 138 def __init__(self, max_value=None, min_value=None, required=True, widget=None, label=None, initial=None): 139 self.max_value, self.min_value = max_value, min_value 140 Field.__init__(self, required, widget, label, initial) 141 142 def clean(self, value): 143 """ 144 Validates that float() can be called on the input. Returns a float. 145 Returns None for empty values. 146 """ 147 super(FloatField, self).clean(value) 148 if not self.required and value in EMPTY_VALUES: 149 return None 150 try: 151 value = float(value) 152 except (ValueError, TypeError): 153 raise ValidationError(gettext(u'Enter a number.')) 154 if self.max_value is not None and value > self.max_value: 155 raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value) 156 if self.min_value is not None and value < self.min_value: 157 raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value) 158 return value 159 160 decimal_re = re.compile(r'^-?(?P<digits>\d+)(\.(?P<decimals>\d+))?$') 161 162 class DecimalField(Field): 163 def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, required=True, widget=None, label=None, initial=None): 164 self.max_value, self.min_value = max_value, min_value 165 self.max_digits, self.decimal_places = max_digits, decimal_places 166 Field.__init__(self, required, widget, label, initial) 167 168 def clean(self, value): 169 """ 170 Validates that the input is a decimal number. Returns a decimal.Decimal 171 instance. Returns None for empty values. Ensures that there are no more 172 than max_digits in the number, and no more than decimal_places digits 173 after the decimal point. 174 """ 175 super(DecimalField, self).clean(value) 176 if not self.required and value in EMPTY_VALUES: 177 return None 178 value = value.strip() 179 match = decimal_re.search(value) 180 if not match: 181 raise ValidationError(gettext(u'Enter a number.')) 182 else: 183 value = Decimal(value) 184 digits = len(match.group('digits') or '') 185 decimals = len(match.group('decimals') or '') 186 if self.max_value is not None and value > self.max_value: 187 raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value) 188 if self.min_value is not None and value < self.min_value: 189 raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value) 190 if self.max_digits is not None and (digits + decimals) > self.max_digits: 191 raise ValidationError(gettext(u'Ensure that there are no more than %s digits in total.') % self.max_digits) 192 if self.decimal_places is not None and decimals > self.decimal_places: 193 raise ValidationError(gettext(u'Ensure that there are no more than %s decimal places.') % self.decimal_places) 194 if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places): 195 raise ValidationError(gettext(u'Ensure that there are no more than %s digits before the decimal point.') % (self.max_digits - self.decimal_places)) 196 return value 197 131 198 DEFAULT_DATE_INPUT_FORMATS = ( 132 199 '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' 133 200 '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006' -
django/contrib/admin/templatetags/admin_list.py
old new 157 157 # Booleans are special: We use images. 158 158 elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField): 159 159 result_repr = _boolean_icon(field_val) 160 # FloatFields are special: Zero-pad the decimals.161 elif isinstance(f, models. FloatField):160 # DecimalFields are special: Zero-pad the decimals. 161 elif isinstance(f, models.DecimalField): 162 162 if field_val is not None: 163 163 result_repr = ('%%.%sf' % f.decimal_places) % field_val 164 164 else: -
django/contrib/admin/views/doc.py
old new 294 294 'CommaSeparatedIntegerField': _('Comma-separated integers'), 295 295 'DateField' : _('Date (without time)'), 296 296 'DateTimeField' : _('Date (with time)'), 297 'DecimalField' : _('Decimal number'), 297 298 'EmailField' : _('E-mail address'), 298 299 'FileField' : _('File path'), 299 300 'FilePathField' : _('File path'), 300 'FloatField' : _(' Decimalnumber'),301 'FloatField' : _('Floating point number'), 301 302 'ForeignKey' : _('Integer'), 302 303 'ImageField' : _('File path'), 303 304 'IntegerField' : _('Integer'), -
django/utils/decimal.py
old new 1 # Copyright (c) 2004 Python Software Foundation. 2 # All rights reserved. 3 4 # Written by Eric Price <eprice at tjhsst.edu> 5 # and Facundo Batista <facundo at taniquetil.com.ar> 6 # and Raymond Hettinger <python at rcn.com> 7 # and Aahz <aahz at pobox.com> 8 # and Tim Peters 9 10 # This module is currently Py2.3 compatible and should be kept that way 11 # unless a major compelling advantage arises. IOW, 2.3 compatibility is 12 # strongly preferred, but not guaranteed. 13 14 # Also, this module should be kept in sync with the latest updates of 15 # the IBM specification as it evolves. Those updates will be treated 16 # as bug fixes (deviation from the spec is a compatibility, usability 17 # bug) and will be backported. At this point the spec is stabilizing 18 # and the updates are becoming fewer, smaller, and less significant. 19 20 """ 21 This is a Py2.3 implementation of decimal floating point arithmetic based on 22 the General Decimal Arithmetic Specification: 23 24 www2.hursley.ibm.com/decimal/decarith.html 25 26 and IEEE standard 854-1987: 27 28 www.cs.berkeley.edu/~ejr/projects/754/private/drafts/854-1987/dir.html 29 30 Decimal floating point has finite precision with arbitrarily large bounds. 31 32 The purpose of the module is to support arithmetic using familiar 33 "schoolhouse" rules and to avoid the some of tricky representation 34 issues associated with binary floating point. The package is especially 35 useful for financial applications or for contexts where users have 36 expectations that are at odds with binary floating point (for instance, 37 in binary floating point, 1.00 % 0.1 gives 0.09999999999999995 instead 38 of the expected Decimal("0.00") returned by decimal floating point). 39 40 Here are some examples of using the decimal module: 41 42 >>> from decimal import * 43 >>> setcontext(ExtendedContext) 44 >>> Decimal(0) 45 Decimal("0") 46 >>> Decimal("1") 47 Decimal("1") 48 >>> Decimal("-.0123") 49 Decimal("-0.0123") 50 >>> Decimal(123456) 51 Decimal("123456") 52 >>> Decimal("123.45e12345678901234567890") 53 Decimal("1.2345E+12345678901234567892") 54 >>> Decimal("1.33") + Decimal("1.27") 55 Decimal("2.60") 56 >>> Decimal("12.34") + Decimal("3.87") - Decimal("18.41") 57 Decimal("-2.20") 58 >>> dig = Decimal(1) 59 >>> print dig / Decimal(3) 60 0.333333333 61 >>> getcontext().prec = 18 62 >>> print dig / Decimal(3) 63 0.333333333333333333 64 >>> print dig.sqrt() 65 1 66 >>> print Decimal(3).sqrt() 67 1.73205080756887729 68 >>> print Decimal(3) ** 123 69 4.85192780976896427E+58 70 >>> inf = Decimal(1) / Decimal(0) 71 >>> print inf 72 Infinity 73 >>> neginf = Decimal(-1) / Decimal(0) 74 >>> print neginf 75 -Infinity 76 >>> print neginf + inf 77 NaN 78 >>> print neginf * inf 79 -Infinity 80 >>> print dig / 0 81 Infinity 82 >>> getcontext().traps[DivisionByZero] = 1 83 >>> print dig / 0 84 Traceback (most recent call last): 85 ... 86 ... 87 ... 88 DivisionByZero: x / 0 89 >>> c = Context() 90 >>> c.traps[InvalidOperation] = 0 91 >>> print c.flags[InvalidOperation] 92 0 93 >>> c.divide(Decimal(0), Decimal(0)) 94 Decimal("NaN") 95 >>> c.traps[InvalidOperation] = 1 96 >>> print c.flags[InvalidOperation] 97 1 98 >>> c.flags[InvalidOperation] = 0 99 >>> print c.flags[InvalidOperation] 100 0 101 >>> print c.divide(Decimal(0), Decimal(0)) 102 Traceback (most recent call last): 103 ... 104 ... 105 ... 106 InvalidOperation: 0 / 0 107 >>> print c.flags[InvalidOperation] 108 1 109 >>> c.flags[InvalidOperation] = 0 110 >>> c.traps[InvalidOperation] = 0 111 >>> print c.divide(Decimal(0), Decimal(0)) 112 NaN 113 >>> print c.flags[InvalidOperation] 114 1 115 >>> 116 """ 117 118 __all__ = [ 119 # Two major classes 120 'Decimal', 'Context', 121 122 # Contexts 123 'DefaultContext', 'BasicContext', 'ExtendedContext', 124 125 # Exceptions 126 'DecimalException', 'Clamped', 'InvalidOperation', 'DivisionByZero', 127 'Inexact', 'Rounded', 'Subnormal', 'Overflow', 'Underflow', 128 129 # Constants for use in setting up contexts 130 'ROUND_DOWN', 'ROUND_HALF_UP', 'ROUND_HALF_EVEN', 'ROUND_CEILING', 131 'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', 132 133 # Functions for manipulating contexts 134 'setcontext', 'getcontext' 135 ] 136 137 import copy 138 139 #Rounding 140 ROUND_DOWN = 'ROUND_DOWN' 141 ROUND_HALF_UP = 'ROUND_HALF_UP' 142 ROUND_HALF_EVEN = 'ROUND_HALF_EVEN' 143 ROUND_CEILING = 'ROUND_CEILING' 144 ROUND_FLOOR = 'ROUND_FLOOR' 145 ROUND_UP = 'ROUND_UP' 146 ROUND_HALF_DOWN = 'ROUND_HALF_DOWN' 147 148 #Rounding decision (not part of the public API) 149 NEVER_ROUND = 'NEVER_ROUND' # Round in division (non-divmod), sqrt ONLY 150 ALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end. 151 152 #Errors 153 154 class DecimalException(ArithmeticError): 155 """Base exception class. 156 157 Used exceptions derive from this. 158 If an exception derives from another exception besides this (such as 159 Underflow (Inexact, Rounded, Subnormal) that indicates that it is only 160 called if the others are present. This isn't actually used for 161 anything, though. 162 163 handle -- Called when context._raise_error is called and the 164 trap_enabler is set. First argument is self, second is the 165 context. More arguments can be given, those being after 166 the explanation in _raise_error (For example, 167 context._raise_error(NewError, '(-x)!', self._sign) would 168 call NewError().handle(context, self._sign).) 169 170 To define a new exception, it should be sufficient to have it derive 171 from DecimalException. 172 """ 173 def handle(self, context, *args): 174 pass 175 176 177 class Clamped(DecimalException): 178 """Exponent of a 0 changed to fit bounds. 179 180 This occurs and signals clamped if the exponent of a result has been 181 altered in order to fit the constraints of a specific concrete 182 representation. This may occur when the exponent of a zero result would 183 be outside the bounds of a representation, or when a large normal 184 number would have an encoded exponent that cannot be represented. In 185 this latter case, the exponent is reduced to fit and the corresponding 186 number of zero digits are appended to the coefficient ("fold-down"). 187 """ 188 189 190 class InvalidOperation(DecimalException): 191 """An invalid operation was performed. 192 193 Various bad things cause this: 194 195 Something creates a signaling NaN 196 -INF + INF 197 0 * (+-)INF 198 (+-)INF / (+-)INF 199 x % 0 200 (+-)INF % x 201 x._rescale( non-integer ) 202 sqrt(-x) , x > 0 203 0 ** 0 204 x ** (non-integer) 205 x ** (+-)INF 206 An operand is invalid 207 """ 208 def handle(self, context, *args): 209 if args: 210 if args[0] == 1: #sNaN, must drop 's' but keep diagnostics 211 return Decimal( (args[1]._sign, args[1]._int, 'n') ) 212 return NaN 213 214 class ConversionSyntax(InvalidOperation): 215 """Trying to convert badly formed string. 216 217 This occurs and signals invalid-operation if an string is being 218 converted to a number and it does not conform to the numeric string 219 syntax. The result is [0,qNaN]. 220 """ 221 222 def handle(self, context, *args): 223 return (0, (0,), 'n') #Passed to something which uses a tuple. 224 225 class DivisionByZero(DecimalException, ZeroDivisionError): 226 """Division by 0. 227 228 This occurs and signals division-by-zero if division of a finite number 229 by zero was attempted (during a divide-integer or divide operation, or a 230 power operation with negative right-hand operand), and the dividend was 231 not zero. 232 233 The result of the operation is [sign,inf], where sign is the exclusive 234 or of the signs of the operands for divide, or is 1 for an odd power of 235 -0, for power. 236 """ 237 238 def handle(self, context, sign, double = None, *args): 239 if double is not None: 240 return (Infsign[sign],)*2 241 return Infsign[sign] 242 243 class DivisionImpossible(InvalidOperation): 244 """Cannot perform the division adequately. 245 246 This occurs and signals invalid-operation if the integer result of a 247 divide-integer or remainder operation had too many digits (would be 248 longer than precision). The result is [0,qNaN]. 249 """ 250 251 def handle(self, context, *args): 252 return (NaN, NaN) 253 254 class DivisionUndefined(InvalidOperation, ZeroDivisionError): 255 """Undefined result of division. 256 257 This occurs and signals invalid-operation if division by zero was 258 attempted (during a divide-integer, divide, or remainder operation), and 259 the dividend is also zero. The result is [0,qNaN]. 260 """ 261 262 def handle(self, context, tup=None, *args): 263 if tup is not None: 264 return (NaN, NaN) #for 0 %0, 0 // 0 265 return NaN 266 267 class Inexact(DecimalException): 268 """Had to round, losing information. 269 270 This occurs and signals inexact whenever the result of an operation is 271 not exact (that is, it needed to be rounded and any discarded digits 272 were non-zero), or if an overflow or underflow condition occurs. The 273 result in all cases is unchanged. 274 275 The inexact signal may be tested (or trapped) to determine if a given 276 operation (or sequence of operations) was inexact. 277 """ 278 pass 279 280 class InvalidContext(InvalidOperation): 281 """Invalid context. Unknown rounding, for example. 282 283 This occurs and signals invalid-operation if an invalid context was 284 detected during an operation. This can occur if contexts are not checked 285 on creation and either the precision exceeds the capability of the 286 underlying concrete representation or an unknown or unsupported rounding 287 was specified. These aspects of the context need only be checked when 288 the values are required to be used. The result is [0,qNaN]. 289 """ 290 291 def handle(self, context, *args): 292 return NaN 293 294 class Rounded(DecimalException): 295 """Number got rounded (not necessarily changed during rounding). 296 297 This occurs and signals rounded whenever the result of an operation is 298 rounded (that is, some zero or non-zero digits were discarded from the 299 coefficient), or if an overflow or underflow condition occurs. The 300 result in all cases is unchanged. 301 302 The rounded signal may be tested (or trapped) to determine if a given 303 operation (or sequence of operations) caused a loss of precision. 304 """ 305 pass 306 307 class Subnormal(DecimalException): 308 """Exponent < Emin before rounding. 309 310 This occurs and signals subnormal whenever the result of a conversion or 311 operation is subnormal (that is, its adjusted exponent is less than 312 Emin, before any rounding). The result in all cases is unchanged. 313 314 The subnormal signal may be tested (or trapped) to determine if a given 315 or operation (or sequence of operations) yielded a subnormal result. 316 """ 317 pass 318 319 class Overflow(Inexact, Rounded): 320 """Numerical overflow. 321 322 This occurs and signals overflow if the adjusted exponent of a result 323 (from a conversion or from an operation that is not an attempt to divide 324 by zero), after rounding, would be greater than the largest value that 325 can be handled by the implementation (the value Emax). 326 327 The result depends on the rounding mode: 328 329 For round-half-up and round-half-even (and for round-half-down and 330 round-up, if implemented), the result of the operation is [sign,inf], 331 where sign is the sign of the intermediate result. For round-down, the 332 result is the largest finite number that can be represented in the 333 current precision, with the sign of the intermediate result. For 334 round-ceiling, the result is the same as for round-down if the sign of 335 the intermediate result is 1, or is [0,inf] otherwise. For round-floor, 336 the result is the same as for round-down if the sign of the intermediate 337 result is 0, or is [1,inf] otherwise. In all cases, Inexact and Rounded 338 will also be raised. 339 """ 340 341 def handle(self, context, sign, *args): 342 if context.rounding in (ROUND_HALF_UP, ROUND_HALF_EVEN, 343 ROUND_HALF_DOWN, ROUND_UP): 344 return Infsign[sign] 345 if sign == 0: 346 if context.rounding == ROUND_CEILING: 347 return Infsign[sign] 348 return Decimal((sign, (9,)*context.prec, 349 context.Emax-context.prec+1)) 350 if sign == 1: 351 if context.rounding == ROUND_FLOOR: 352 return Infsign[sign] 353 return Decimal( (sign, (9,)*context.prec, 354 context.Emax-context.prec+1)) 355 356 357 class Underflow(Inexact, Rounded, Subnormal): 358 """Numerical underflow with result rounded to 0. 359 360 This occurs and signals underflow if a result is inexact and the 361 adjusted exponent of the result would be smaller (more negative) than 362 the smallest value that can be handled by the implementation (the value 363 Emin). That is, the result is both inexact and subnormal. 364 365 The result after an underflow will be a subnormal number rounded, if 366 necessary, so that its exponent is not less than Etiny. This may result 367 in 0 with the sign of the intermediate result and an exponent of Etiny. 368 369 In all cases, Inexact, Rounded, and Subnormal will also be raised. 370 """ 371 372 # List of public traps and flags 373 _signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded, 374 Underflow, InvalidOperation, Subnormal] 375 376 # Map conditions (per the spec) to signals 377 _condition_map = {ConversionSyntax:InvalidOperation, 378 DivisionImpossible:InvalidOperation, 379 DivisionUndefined:InvalidOperation, 380 InvalidContext:InvalidOperation} 381 382 ##### Context Functions ####################################### 383 384 # The getcontext() and setcontext() function manage access to a thread-local 385 # current context. Py2.4 offers direct support for thread locals. If that 386 # is not available, use threading.currentThread() which is slower but will 387 # work for older Pythons. If threads are not part of the build, create a 388 # mock threading object with threading.local() returning the module namespace. 389 390 try: 391 import threading 392 except ImportError: 393 # Python was compiled without threads; create a mock object instead 394 import sys 395 class MockThreading: 396 def local(self, sys=sys): 397 return sys.modules[__name__] 398 threading = MockThreading() 399 del sys, MockThreading 400 401 try: 402 threading.local 403 404 except AttributeError: 405 406 #To fix reloading, force it to create a new context 407 #Old contexts have different exceptions in their dicts, making problems. 408 if hasattr(threading.currentThread(), '__decimal_context__'): 409 del threading.currentThread().__decimal_context__ 410 411 def setcontext(context): 412 """Set this thread's context to context.""" 413 if context in (DefaultContext, BasicContext, ExtendedContext): 414 context = context.copy() 415 context.clear_flags() 416 threading.currentThread().__decimal_context__ = context 417 418 def getcontext(): 419 """Returns this thread's context. 420 421 If this thread does not yet have a context, returns 422 a new context and sets this thread's context. 423 New contexts are copies of DefaultContext. 424 """ 425 try: 426 return threading.currentThread().__decimal_context__ 427 except AttributeError: 428 context = Context() 429 threading.currentThread().__decimal_context__ = context 430 return context 431 432 else: 433 434 local = threading.local() 435 if hasattr(local, '__decimal_context__'): 436 del local.__decimal_context__ 437 438 def getcontext(_local=local): 439 """Returns this thread's context. 440 441 If this thread does not yet have a context, returns 442 a new context and sets this thread's context. 443
