Hello community, here is the log from the commit of package python-Faker for openSUSE:Factory checked in at 2019-08-05 11:18:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-Faker (Old) and /work/SRC/openSUSE:Factory/.python-Faker.new.4126 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Faker" Mon Aug 5 11:18:08 2019 rev:12 rq:718191 version:2.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-Faker/python-Faker.changes 2019-05-27 08:36:01.619126137 +0200 +++ /work/SRC/openSUSE:Factory/.python-Faker.new.4126/python-Faker.changes 2019-08-05 11:18:10.502516115 +0200 @@ -1,0 +2,18 @@ +Wed Jul 24 09:39:48 UTC 2019 - Marketa Calabkova <[email protected]> + +- update to version 2.0.0 + * Breaking change: Only allow providers to use OrderedDict s, to + avoid any more PYTHONHASHSEED problems. + * Remove some validations from Faker and delegate it to an + external library, validators. + * Add an "Invalid SSN" generator to the en_US SSN Provider. + * Loosen version restrictions on freezegun and random2. + * Add date_of_birth and sex argument to pesel Provider (pl_PL). + * Fix datetime parsing on environments with negative offsets. + +------------------------------------------------------------------- +Fri Jul 19 20:16:51 UTC 2019 - Sean Marlow <[email protected]> + +- Update ipaddress requirements. + +------------------------------------------------------------------- Old: ---- Faker-1.0.7.tar.gz New: ---- Faker-2.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-Faker.spec ++++++ --- /var/tmp/diff_new_pack.2sZXjC/_old 2019-08-05 11:18:11.346515769 +0200 +++ /var/tmp/diff_new_pack.2sZXjC/_new 2019-08-05 11:18:11.346515769 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define oldpython python Name: python-Faker -Version: 1.0.7 +Version: 2.0.0 Release: 0 Summary: Python package that generates fake data License: MIT @@ -27,19 +27,19 @@ URL: https://github.com/joke2k/faker Source: https://files.pythonhosted.org/packages/source/F/Faker/Faker-%{version}.tar.gz BuildRequires: %{python_module UkPostcodeParser >= 1.1.1} -BuildRequires: %{python_module email_validator >= 1.0.2} -BuildRequires: %{python_module freezegun >= 0.3.11} +BuildRequires: %{python_module freezegun} BuildRequires: %{python_module mock} -BuildRequires: %{python_module more-itertools} BuildRequires: %{python_module pytest >= 3.8.0} +BuildRequires: %{python_module pytest-runner} BuildRequires: %{python_module python-dateutil >= 2.4} -BuildRequires: %{python_module random2 >= 1.0.1} +BuildRequires: %{python_module random2} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module six >= 1.10} BuildRequires: %{python_module text-unidecode >= 1.2} +BuildRequires: %{python_module validators >= 0.13.0} BuildRequires: fdupes -BuildRequires: python-ipaddress BuildRequires: python-rpm-macros +BuildRequires: python2-ipaddress Requires: python-python-dateutil >= 2.4 Requires: python-setuptools Requires: python-six >= 1.10 @@ -47,9 +47,8 @@ Requires(post): update-alternatives Requires(postun): update-alternatives BuildArch: noarch -BuildRequires: %{python_module pytest-runner} %ifpython2 -Requires: python-ipaddress +Requires: python2-ipaddress Obsoletes: %{oldpython}-fake-factory < %{version} Provides: %{oldpython}-fake-factory = %{version} %endif ++++++ Faker-1.0.7.tar.gz -> Faker-2.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/CHANGELOG.rst new/Faker-2.0.0/CHANGELOG.rst --- old/Faker-1.0.7/CHANGELOG.rst 2019-05-14 17:55:14.000000000 +0200 +++ new/Faker-2.0.0/CHANGELOG.rst 2019-07-15 16:41:26.000000000 +0200 @@ -1,6 +1,23 @@ Changelog ========= +`2.0.0 - 15-July-2019 <https://github.com/joke2k/faker/compare/v2.0.0...v1.0.8>`__ +---------------------------------------------------------------------------------- +* Breaking change: Only allow providers to use ``OrderedDict`` s, to avoid any more ``PYTHONHASHSEED`` problems. Thanks @adamchainz. + +`1.0.8 - 15-July-2019 <https://github.com/joke2k/faker/compare/v1.0.7...v1.0.8>`__ +---------------------------------------------------------------------------------- + +* Rename ``pyint`` ``min`` and ``max`` to ``min_value`` and ``max_value``. Thanks @francoisfreitag. +* Remove some validations from Faker and delegate it to an external library, ``validators``. Thanks @kingbuzzman. +* Add an "Invalid SSN" generator to the ``en_US`` SSN Provider. Thanks @darrylwhiting. +* Include "Praia" as street_prefix in ``pr_BR`` address Provider. Thanks @G5Olivieri. +* Loosen version restrictions on ``freezegun`` and ``random2``. Thanks @timokau. +* Add SSN provider for ``es_MX``. Thanks @mrfunnyshoes. +* Add ``pwz`` generator for ``pl_PL``. Thanks @torm89. +* Add ``date_of_birth`` and ``sex`` argument to ``pesel`` Provider (`pl_PL`). Thanks @torm89. +* Fix datetime parsing on environments with negative offsets. Thanks @bluesheeptoken. + `1.0.7 - 14-May-2019 <https://github.com/joke2k/faker/compare/v1.0.6...v1.0.7>`__ --------------------------------------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/Faker.egg-info/PKG-INFO new/Faker-2.0.0/Faker.egg-info/PKG-INFO --- old/Faker-1.0.7/Faker.egg-info/PKG-INFO 2019-05-14 18:11:36.000000000 +0200 +++ new/Faker-2.0.0/Faker.egg-info/PKG-INFO 2019-07-15 16:58:24.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: Faker -Version: 1.0.7 +Version: 2.0.0 Summary: Faker is a Python package that generates fake data for you. Home-page: https://github.com/joke2k/faker Author: joke2k diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/Faker.egg-info/SOURCES.txt new/Faker-2.0.0/Faker.egg-info/SOURCES.txt --- old/Faker-1.0.7/Faker.egg-info/SOURCES.txt 2019-05-14 18:11:36.000000000 +0200 +++ new/Faker-2.0.0/Faker.egg-info/SOURCES.txt 2019-07-15 16:58:24.000000000 +0200 @@ -3,6 +3,7 @@ LICENSE.txt MANIFEST.in README.rst +RELEASE_PROCESS.rst VERSION setup.cfg setup.py @@ -342,6 +343,7 @@ faker/providers/ssn/en_US/__init__.py faker/providers/ssn/es_CA/__init__.py faker/providers/ssn/es_ES/__init__.py +faker/providers/ssn/es_MX/__init__.py faker/providers/ssn/et_EE/__init__.py faker/providers/ssn/fi_FI/__init__.py faker/providers/ssn/fr_CH/__init__.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/MANIFEST.in new/Faker-2.0.0/MANIFEST.in --- old/Faker-1.0.7/MANIFEST.in 2019-04-15 17:09:53.000000000 +0200 +++ new/Faker-2.0.0/MANIFEST.in 2019-07-15 16:44:46.000000000 +0200 @@ -2,6 +2,7 @@ include LICENSE.txt include CONTRIBUTING.rst include CHANGELOG.rst +include RELEASE_PROCESS.rst include VERSION recursive-include tests *.json recursive-include tests *.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/PKG-INFO new/Faker-2.0.0/PKG-INFO --- old/Faker-1.0.7/PKG-INFO 2019-05-14 18:11:37.000000000 +0200 +++ new/Faker-2.0.0/PKG-INFO 2019-07-15 16:58:25.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: Faker -Version: 1.0.7 +Version: 2.0.0 Summary: Faker is a Python package that generates fake data for you. Home-page: https://github.com/joke2k/faker Author: joke2k diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/RELEASE_PROCESS.rst new/Faker-2.0.0/RELEASE_PROCESS.rst --- old/Faker-1.0.7/RELEASE_PROCESS.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/Faker-2.0.0/RELEASE_PROCESS.rst 2019-07-15 16:35:50.000000000 +0200 @@ -0,0 +1,11 @@ +Release Process +--------------- + +1. Compile entries in ``CHANGELOG.rst``. Each entry should: + + * Be in the past tense (eg "Fix datetime on Windows") + * End with acknowledging the PR author(s): "Thanks @<github username>." + +2. Run ``bumpversion <patch|minor|major>``. +3. Check the commit generated by ``bumpversion``, then ``git push``. +4. Run ``make release``. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/VERSION new/Faker-2.0.0/VERSION --- old/Faker-1.0.7/VERSION 2019-05-14 18:10:06.000000000 +0200 +++ new/Faker-2.0.0/VERSION 2019-07-15 16:57:52.000000000 +0200 @@ -1 +1 @@ -1.0.7 +2.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/__init__.py new/Faker-2.0.0/faker/__init__.py --- old/Faker-1.0.7/faker/__init__.py 2019-05-14 18:10:06.000000000 +0200 +++ new/Faker-2.0.0/faker/__init__.py 2019-07-15 16:57:52.000000000 +0200 @@ -1,6 +1,6 @@ from faker.generator import Generator # noqa F401 from faker.factory import Factory # noqa F401 -VERSION = '1.0.7' +VERSION = '2.0.0' Faker = Factory.create diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/__init__.py new/Faker-2.0.0/faker/providers/__init__.py --- old/Faker-1.0.7/faker/providers/__init__.py 2019-04-15 17:09:53.000000000 +0200 +++ new/Faker-2.0.0/faker/providers/__init__.py 2019-07-15 16:39:47.000000000 +0200 @@ -3,6 +3,8 @@ import re import string +from collections import OrderedDict + from faker.utils.distribution import choices_distribution, choices_distribution_unique @@ -178,6 +180,9 @@ return self.generator.random.choice(string.ascii_uppercase) def random_elements(self, elements=('a', 'b', 'c'), length=None, unique=False): + if isinstance(elements, dict) and not isinstance(elements, OrderedDict): + raise ValueError("Use OrderedDict only to avoid dependency on PYTHONHASHSEED (See #363).") + fn = choices_distribution_unique if unique else choices_distribution if length is None: @@ -208,10 +213,15 @@ """ Returns a list of random, non-unique elements from a passed object. - If `elements` is a dictionary, the value will be used as - a weighting element. For example:: + If `elements` is an OrderedDict, the value will be used as a weighting + element. For example:: - random_element({"{{variable_1}}": 0.5, "{{variable_2}}": 0.2, "{{variable_3}}": 0.2, "{{variable_4}}": 0.1}) + random_element(OrderedDict([ + ("{{variable_1}}", 0.5), + ("{{variable_2}}", 0.2), + ("{{variable_3}}", 0.2), + ("{{variable_4}}": 0.1) + ]) will have the following distribution: * `variable_1`: 50% probability @@ -226,10 +236,15 @@ """ Returns a random element from a passed object. - If `elements` is a dictionary, the value will be used as - a weighting element. For example:: + If `elements` is an OrderedDict, the value will be used as a weighting + element. For example:: - random_element({"{{variable_1}}": 0.5, "{{variable_2}}": 0.2, "{{variable_3}}": 0.2, "{{variable_4}}": 0.1}) + random_element(OrderedDict([ + ("{{variable_1}}", 0.5), + ("{{variable_2}}", 0.2), + ("{{variable_3}}", 0.2), + ("{{variable_4}}": 0.1) + ]) will have the following distribution: * `variable_1`: 50% probability @@ -238,6 +253,7 @@ * `variable_4`: 10% probability """ + return self.random_elements(elements, length=1)[0] def random_sample(self, elements=('a', 'b', 'c'), length=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/address/no_NO/__init__.py new/Faker-2.0.0/faker/providers/address/no_NO/__init__.py --- old/Faker-1.0.7/faker/providers/address/no_NO/__init__.py 2019-03-05 18:07:52.000000000 +0100 +++ new/Faker-2.0.0/faker/providers/address/no_NO/__init__.py 2019-07-15 16:39:47.000000000 +0200 @@ -1,6 +1,8 @@ # coding=utf-8 from __future__ import unicode_literals +from collections import OrderedDict + from .. import Provider as AddressProvider @@ -26,9 +28,16 @@ street_address_formats = ('{{street_name}} {{building_number}}',) address_formats = ('{{street_address}}, {{postcode}} {{city}}',) building_number_formats = ('%', '%', '%', '%?', '##', '##', '##?', '###') - building_number_suffixes = { - 'A': 0.2, 'B': 0.2, 'C': 0.2, 'D': 0.1, 'E': 0.1, 'F': 0.1, 'G': 0.05, - 'H': 0.05} + building_number_suffixes = OrderedDict([ + ('A', 0.2), + ('B', 0.2), + ('C', 0.2), + ('D', 0.1), + ('E', 0.1), + ('F', 0.1), + ('G', 0.05), + ('H', 0.05), + ]) postcode_formats = ('####',) def building_number(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/address/pt_BR/__init__.py new/Faker-2.0.0/faker/providers/address/pt_BR/__init__.py --- old/Faker-1.0.7/faker/providers/address/pt_BR/__init__.py 2019-03-05 18:07:52.000000000 +0100 +++ new/Faker-2.0.0/faker/providers/address/pt_BR/__init__.py 2019-07-12 17:17:07.000000000 +0200 @@ -53,6 +53,7 @@ 'Passarela', 'Pátio', 'Praça', + 'Praia', 'Quadra', 'Recanto', 'Residencial', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/date_time/__init__.py new/Faker-2.0.0/faker/providers/date_time/__init__.py --- old/Faker-1.0.7/faker/providers/date_time/__init__.py 2019-04-12 17:10:07.000000000 +0200 +++ new/Faker-2.0.0/faker/providers/date_time/__init__.py 2019-07-15 16:35:50.000000000 +0200 @@ -6,7 +6,6 @@ from calendar import timegm from datetime import timedelta, MAXYEAR -from time import time from dateutil import relativedelta from dateutil.tz import tzlocal, tzutc @@ -1474,7 +1473,7 @@ @classmethod def _parse_end_datetime(cls, value): if value is None: - return int(time()) + return datetime_to_timestamp(datetime.now()) return cls._parse_date_time(value) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/person/et_EE/__init__.py new/Faker-2.0.0/faker/providers/person/et_EE/__init__.py --- old/Faker-1.0.7/faker/providers/person/et_EE/__init__.py 2017-12-05 16:57:34.000000000 +0100 +++ new/Faker-2.0.0/faker/providers/person/et_EE/__init__.py 2019-07-15 16:39:47.000000000 +0200 @@ -1,5 +1,8 @@ # coding=utf-8 from __future__ import unicode_literals + +from collections import OrderedDict + from .. import Provider as PersonProvider @@ -9,13 +12,20 @@ # About 70% of the population are Estonians and about 25% are Russians est_rat = 0.7 rus_rat = 1.0 - est_rat - formats = {'{{first_name_est}} {{last_name_est}}': est_rat, - '{{first_name_rus}} {{last_name_rus}}': rus_rat} + formats = OrderedDict([ + ('{{first_name_est}} {{last_name_est}}', est_rat), + ('{{first_name_rus}} {{last_name_rus}}', rus_rat), + ]) + + formats_male = OrderedDict([ + ('{{first_name_male_est}} {{last_name_est}}', est_rat), + ('{{first_name_male_rus}} {{last_name_rus}}', rus_rat), + ]) - formats_male = {'{{first_name_male_est}} {{last_name_est}}': est_rat, - '{{first_name_male_rus}} {{last_name_rus}}': rus_rat} - formats_female = {'{{first_name_female_est}} {{last_name_est}}': est_rat, - '{{first_name_female_rus}} {{last_name_rus}}': rus_rat} + formats_female = OrderedDict([ + ('{{first_name_female_est}} {{last_name_est}}', est_rat), + ('{{first_name_female_rus}} {{last_name_rus}}', rus_rat), + ]) prefixes_neutral = ('doktor', 'dr', 'prof') prefixes_male = ('härra', 'hr') + prefixes_neutral diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/person/pl_PL/__init__.py new/Faker-2.0.0/faker/providers/person/pl_PL/__init__.py --- old/Faker-1.0.7/faker/providers/person/pl_PL/__init__.py 2019-05-14 17:28:28.000000000 +0200 +++ new/Faker-2.0.0/faker/providers/person/pl_PL/__init__.py 2019-07-15 16:35:50.000000000 +0200 @@ -21,28 +21,6 @@ return check_digit -def generate_pesel_checksum_value(pesel_digits): - """ - Calculates and returns a control digit for given PESEL. - """ - checksum_values = [9, 7, 3, 1, 9, 7, 3, 1, 9, 7] - - checksum = sum((int(a) * b for a, b in zip(list(pesel_digits), checksum_values))) - - return checksum % 10 - - -def checksum_pesel_number(pesel_digits): - """ - Calculates and returns True if PESEL is valid. - """ - checksum_values = [1, 3, 7, 9, 1, 3, 7, 9, 1, 3, 1] - - checksum = sum((int(a) * b for a, b in zip(list(pesel_digits), checksum_values))) - - return checksum % 10 == 0 - - class Provider(PersonProvider): formats = ( '{{first_name}} {{last_name}}', @@ -725,30 +703,74 @@ return ''.join(str(character) for character in identity) - def pesel(self): + @staticmethod + def pesel_compute_check_digit(pesel): + checksum_values = [9, 7, 3, 1, 9, 7, 3, 1, 9, 7] + return sum(int(a) * b for a, b in zip(pesel, checksum_values)) % 10 + + def pesel(self, date_of_birth=None, sex=None): """ Returns 11 characters of Universal Electronic System for Registration of the Population. Polish: Powszechny Elektroniczny System Ewidencji Ludności. PESEL has 11 digits which identifies just one person. - Month: if person was born in 1900-2000, december is 12. If person was born > 2000, we have to add 20 to month, - so december is 32. - Person id: last digit identifies person's sex. Even for females, odd for males. + pesel_date: if person was born in 1900-2000, december is 12. If person was born > 2000, we have to add 20 to + month, so december is 32. + pesel_sex: last digit identifies person's sex. Even for females, odd for males. https://en.wikipedia.org/wiki/PESEL """ + if date_of_birth is None: + date_of_birth = self.generator.date_of_birth() + + pesel_date = '{year}{month:02d}{day:02d}'.format( + year=date_of_birth.year, day=date_of_birth.day, + month=date_of_birth.month if date_of_birth.year < 2000 else date_of_birth.month + 20) + pesel_date = pesel_date[2:] + + pesel_core = ''.join(map(str, (self.random_digit() for _ in range(3)))) + pesel_sex = self.random_digit() + + if (sex == 'M' and pesel_sex % 2 == 0) or (sex == 'F' and pesel_sex % 2 == 1): + pesel_sex = (pesel_sex + 1) % 10 + + pesel = '{date}{core}{sex}'.format(date=pesel_date, core=pesel_core, sex=pesel_sex) + pesel += str(self.pesel_compute_check_digit(pesel)) + + return pesel + + @staticmethod + def pwz_doctor_compute_check_digit(x): + return sum((i+1)*d for i, d in enumerate(x)) % 11 + + def pwz_doctor(self): + """ + Function generates an identification number for medical doctors + Polish: Prawo Wykonywania Zawodu (PWZ) + + https://www.nil.org.pl/rejestry/centralny-rejestr-lekarzy/zasady-weryfikowania-nr-prawa-wykonywania-zawodu + """ + core = [self.random_digit() for _ in range(6)] + check_digit = self.pwz_doctor_compute_check_digit(core) + + if check_digit == 0: + core[-1] = (core[-1] + 1) % 10 + check_digit = self.pwz_doctor_compute_check_digit(core) + + return '{}{}'.format(check_digit, ''.join(map(str, core))) + + def pwz_nurse(self, kind='nurse'): + """ + Function generates an identification number for nurses and midwives + Polish: Prawo Wykonywania Zawodu (PWZ) - birth = self.generator.date_of_birth() + http://arch.nipip.pl/index.php/prawo/uchwaly/naczelnych-rad/w-roku-2015/posiedzenie-15-17-grudnia/3664-uchwala- + nr-381-vi-2015-w-sprawie-trybu-postepowania-dotyczacego-stwierdzania-i-przyznawania-prawa-wykonywania-zawodu-pi + elegniarki-i-zawodu-poloznej-oraz-sposobu-prowadzenia-rejestru-pielegniarek-i-rejestru-poloznych-przez-okregowe + -rady-pielegniarek-i-polo + """ + region = self.random_int(1, 45) + core = [self.random_digit() for _ in range(5)] + kind_char = 'A' if kind == 'midwife' else 'P' - year_pesel = str(birth.year)[-2:] - month_pesel = birth.month if birth.year < 2000 else birth.month + 20 - day_pesel = birth.day - person_id = self.random_int(1000, 9999) - - current_pesel = '{year}{month:02d}{day:02d}{person_id:04d}'.format(year=year_pesel, month=month_pesel, - day=day_pesel, - person_id=person_id) - - checksum_value = generate_pesel_checksum_value(current_pesel) - return '{pesel_without_checksum}{checksum_value}'.format(pesel_without_checksum=current_pesel, - checksum_value=checksum_value) + return '{:02d}{}{}'.format(region, ''.join(map(str, core)), kind_char) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/python/__init__.py new/Faker-2.0.0/faker/providers/python/__init__.py --- old/Faker-1.0.7/faker/providers/python/__init__.py 2019-05-14 17:28:28.000000000 +0200 +++ new/Faker-2.0.0/faker/providers/python/__init__.py 2019-07-12 17:17:07.000000000 +0200 @@ -71,8 +71,8 @@ self.random_number(right_digits), )) - def pyint(self, min=0, max=9999, step=1): - return self.generator.random_int(min, max, step=step) + def pyint(self, min_value=0, max_value=9999, step=1): + return self.generator.random_int(min_value, max_value, step=step) def pydecimal(self, left_digits=None, right_digits=None, positive=False, min_value=None, max_value=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/ssn/en_US/__init__.py new/Faker-2.0.0/faker/providers/ssn/en_US/__init__.py --- old/Faker-1.0.7/faker/providers/ssn/en_US/__init__.py 2019-03-05 18:07:52.000000000 +0100 +++ new/Faker-2.0.0/faker/providers/ssn/en_US/__init__.py 2019-07-12 17:17:07.000000000 +0200 @@ -6,6 +6,7 @@ class Provider(BaseProvider): + INVALID_SSN_TYPE = 'INVALID_SSN' SSN_TYPE = 'SSN' ITIN_TYPE = 'ITIN' EIN_TYPE = 'EIN' @@ -139,6 +140,68 @@ ein = "{0:s}-{1:07d}".format(ein_prefix, sequence) return ein + def invalid_ssn(self): + """ Generate a random invalid United States Social Security Identification Number (SSN). + + Invalid SSNs have the following characteristics: + Cannot begin with the number 9 + Cannot begin with 666 in positions 1 - 3 + Cannot begin with 000 in positions 1 - 3 + Cannot contain 00 in positions 4 - 5 + Cannot contain 0000 in positions 6 - 9 + + https://www.ssa.gov/kc/SSAFactSheet--IssuingSSNs.pdf + + Additionally, return an invalid SSN that is NOT a valid ITIN by excluding certain ITIN related "group" values + """ + itin_group_numbers = [ + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 90, + 91, + 92, + 94, + 95, + 96, + 97, + 98, + 99] + area = self.random_int(min=0, max=999) + if area < 900 and area not in {666, 0}: + random_group_or_serial = self.random_int(min=1, max=1000) + if random_group_or_serial <= 500: + group = 0 + serial = self.random_int(0, 9999) + else: + group = self.random_int(0, 99) + serial = 0 + elif area in {666, 0}: + group = self.random_int(0, 99) + serial = self.random_int(0, 9999) + else: + group = random.choice([x for x in range(0, 100) if x not in itin_group_numbers]) + serial = self.random_int(0, 9999) + + invalid_ssn = "{0:03d}-{1:02d}-{2:04d}".format(area, group, serial) + return invalid_ssn + def ssn(self, taxpayer_identification_number_type=SSN_TYPE): """ Generate a random United States Taxpayer Identification Number of the specified type. @@ -149,6 +212,8 @@ return self.itin() elif taxpayer_identification_number_type == self.EIN_TYPE: return self.ein() + elif taxpayer_identification_number_type == self.INVALID_SSN_TYPE: + return self.invalid_ssn() elif taxpayer_identification_number_type == self.SSN_TYPE: # Certain numbers are invalid for United States Social Security @@ -166,4 +231,5 @@ return ssn else: - raise ValueError("taxpayer_identification_number_type must be one of 'SSN', 'EIN', or 'ITIN'.") + raise ValueError("taxpayer_identification_number_type must be one of 'SSN', 'EIN', 'ITIN'," + " or 'INVALID_SSN'.") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/faker/providers/ssn/es_MX/__init__.py new/Faker-2.0.0/faker/providers/ssn/es_MX/__init__.py --- old/Faker-1.0.7/faker/providers/ssn/es_MX/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/Faker-2.0.0/faker/providers/ssn/es_MX/__init__.py 2019-07-12 17:17:07.000000000 +0200 @@ -0,0 +1,254 @@ +# coding=utf-8 +""" +SSN provider for es_MX. + +This module adds a provider for mexican SSN, along with Unique Population +Registry Code (CURP) and Federal Taxpayer Registry ID (RFC). +""" + + +from __future__ import unicode_literals + +import random +import string + +from .. import Provider as BaseProvider + + +ALPHABET = string.ascii_uppercase +ALPHANUMERIC = string.digits + ALPHABET +VOWELS = "AEIOU" +CONSONANTS = [ + letter + for letter in ALPHABET + if letter not in VOWELS +] + +# https://es.wikipedia.org/wiki/Plantilla:Abreviaciones_de_los_estados_de_M%C3%A9xico +STATES_RENAPO = [ + "AS", + "BC", + "BS", + "CC", + "CS", + "CH", + "DF", + "CL", + "CM", + "DG", + "GT", + "GR", + "HG", + "JC", + "MC", + "MN", + "MS", + "NT", + "NL", + "OC", + "PL", + "QO", + "QR", + "SP", + "SL", + "SR", + "TC", + "TS", + "TL", + "VZ", + "YN", + "ZS", + "NE", # Foreign Born +] + +FORBIDDEN_WORDS = { + "BUEI": "BUEX", + "BUEY": "BUEX", + "CACA": "CACX", + "CACO": "CACX", + "CAGA": "CAGX", + "CAGO": "CAGX", + "CAKA": "CAKX", + "CAKO": "CAKX", + "COGE": "COGX", + "COJA": "COJX", + "COJE": "COJX", + "COJI": "COJX", + "COJO": "COJX", + "CULO": "CULX", + "FETO": "FETX", + "GUEY": "GUEX", + "JOTO": "JOTX", + "KACA": "KACX", + "KACO": "KACX", + "KAGA": "KAGX", + "KAGO": "KAGX", + "KOGE": "KOGX", + "KOJO": "KOJX", + "KAKA": "KAKX", + "KULO": "KULX", + "MAME": "MAMX", + "MAMO": "MAMX", + "MEAR": "MEAX", + "MEAS": "MEAX", + "MEON": "MEOX", + "MION": "MIOX", + "MOCO": "MOCX", + "MULA": "MULX", + "PEDA": "PEDX", + "PEDO": "PEDX", + "PENE": "PENX", + "PUTA": "PUTX", + "PUTO": "PUTX", + "QULO": "QULX", + "RATA": "RATX", + "RUIN": "RUIN", +} + +CURP_CHARACTERS = "0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ" + + +def _reduce_digits(number): + """ + Sum of digits of a number until sum becomes single digit. + + Example: + 658 => 6 + 5 + 8 = 19 => 1 + 9 = 10 => 1 + """ + if number == 0: + return 0 + if number % 9 == 0: + return 9 + + return number % 9 + + +def ssn_checksum(digits): + """ + Calculate the checksum for the mexican SSN (IMSS). + """ + return -sum( + _reduce_digits(n * (i % 2 + 1)) + for i, n in enumerate(digits) + ) % 10 + + +def curp_checksum(characters): + """ + Calculate the checksum for the mexican CURP. + """ + start = 18 + return -sum( + (start - i) * CURP_CHARACTERS.index(n) + for i, n in enumerate(characters) + ) % 10 + + +class Provider(BaseProvider): + """ + A Faker provider for the Mexican SSN, RFC and CURP + """ + ssn_formats = ("###########",) + + def ssn(self): + """ + Mexican Social Security Number, as given by IMSS. + + :return: a random Mexican SSN + """ + office = self.random_int(min=1, max=99) + birth_year = self.random_int(min=0, max=99) + start_year = self.random_int(min=0, max=99) + serial = self.random_int(min=1, max=9999) + + num = "{0:02d}{1:02d}{2:02d}{3:04d}".format( + office, + start_year, + birth_year, + serial, + ) + + check = ssn_checksum(map(int, num)) + num += str(check) + + return num + + def curp(self): + """ + See https://es.wikipedia.org/wiki/Clave_%C3%9Anica_de_Registro_de_Poblaci%C3%B3n. + + :return: a random Mexican CURP (Unique Population Registry Code) + """ + birthday = self.generator.date_of_birth() + + first_surname = random.choice(ALPHABET) + random.choice(VOWELS) + second_surname = random.choice(ALPHABET) + given_name = random.choice(ALPHABET) + name_initials = first_surname + second_surname + given_name + + birth_date = birthday.strftime("%y%m%d") + gender = random.choice("HM") + state = random.choice(STATES_RENAPO) + first_surname_inside = random.choice(CONSONANTS) + second_surname_inside = random.choice(CONSONANTS) + given_name_inside = random.choice(ALPHABET) + + # This character is assigned to avoid duplicity + # It's normally '0' for those born < 2000 + # and 'A' for those born >= 2000 + assigned_character = "0" if birthday.year < 2000 else "A" + + name_initials = FORBIDDEN_WORDS.get(name_initials, name_initials) + + random_curp = ( + name_initials + + birth_date + + gender + + state + + first_surname_inside + + second_surname_inside + + given_name_inside + + assigned_character + ) + + random_curp += str(curp_checksum(random_curp)) + + return random_curp + + def rfc(self, natural=True): + """ + See https://es.wikipedia.org/wiki/Registro_Federal_de_Contribuyentes + + :param natural: Whether to return the RFC of a natural person. + Otherwise return the RFC of a legal person. + :type natural: bool + :return: a random Mexican RFC + """ + birthday = self.generator.date_of_birth() + + if natural: + first_surname = random.choice(ALPHABET) + random.choice(VOWELS) + second_surname = random.choice(ALPHABET) + given_name = random.choice(ALPHABET) + name_initials = first_surname + second_surname + given_name + else: + name_initials = ( + self.random_uppercase_letter() + + self.random_uppercase_letter() + + self.random_uppercase_letter() + ) + + birth_date = birthday.strftime("%y%m%d") + disambiguation_code = ( + random.choice(ALPHANUMERIC) + + random.choice(ALPHANUMERIC) + + random.choice(ALPHANUMERIC) + ) + + random_rfc = ( + name_initials + + birth_date + + disambiguation_code + ) + + return random_rfc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/setup.py new/Faker-2.0.0/setup.py --- old/Faker-1.0.7/setup.py 2019-05-14 17:28:28.000000000 +0200 +++ new/Faker-2.0.0/setup.py 2019-07-12 18:06:49.000000000 +0200 @@ -69,13 +69,15 @@ "text-unidecode==1.2", ], tests_require=[ - "email_validator>=1.0.1,<1.1.0", + "validators>=0.13.0", "ukpostcodeparser>=1.1.1", "mock ; python_version < '3.3'", "pytest>=3.8.0,<3.9", "more-itertools<6.0.0 ; python_version < '3.0'", - "random2==1.0.1", - "freezegun==0.3.11", + # restricted because they may drop python2 support in future versions + # https://github.com/joke2k/faker/issues/970 + "random2<1.1", + "freezegun<0.4", ], extras_require={ ':python_version<"3.3"': [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/tests/providers/test_date_time.py new/Faker-2.0.0/tests/providers/test_date_time.py --- old/Faker-1.0.7/tests/providers/test_date_time.py 2019-04-12 17:10:07.000000000 +0200 +++ new/Faker-2.0.0/tests/providers/test_date_time.py 2019-07-15 16:35:50.000000000 +0200 @@ -3,10 +3,13 @@ from datetime import date, datetime, timedelta, tzinfo from datetime import time as datetime_time -import time -import unittest +import os +import platform +import pytest import random import sys +import time +import unittest import six @@ -17,8 +20,6 @@ from faker.providers.date_time.ar_EG import Provider as EgProvider from faker.providers.date_time.hy_AM import Provider as HyAmProvider -import pytest - def is64bit(): return sys.maxsize > 2**32 @@ -448,7 +449,7 @@ from faker.providers.date_time import datetime_to_timestamp for _ in range(100): - now = datetime.now(utc).replace(microsecond=0) + now = datetime.now().replace(microsecond=0) epoch_start = datetime(1970, 1, 1, tzinfo=utc) # Ensure doubly-constrained unix_times are generated correctly @@ -490,6 +491,14 @@ self.assertIsInstance(constrained_unix_time, int) self.assertBetween(constrained_unix_time, 0, datetime_to_timestamp(now)) + # Ensure it does not throw error with startdate='now' for machines with negative offset + if platform.system() != 'Windows': + os.environ['TZ'] = 'Europe/Paris' + time.tzset() + self.factory.unix_time(start_datetime='now') + if platform.system() != 'Windows': + del os.environ['TZ'] + class TestPlPL(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/tests/providers/test_internet.py new/Faker-2.0.0/tests/providers/test_internet.py --- old/Faker-1.0.7/tests/providers/test_internet.py 2019-05-14 17:28:28.000000000 +0200 +++ new/Faker-2.0.0/tests/providers/test_internet.py 2019-07-12 17:17:07.000000000 +0200 @@ -12,7 +12,7 @@ import pytest import six -from email_validator import validate_email +from validators import email as validate_email from faker import Faker from faker.providers.person.ja_JP import Provider as JaProvider @@ -120,7 +120,7 @@ def test_email(self): email = self.factory.email() - validate_email(email, check_deliverability=False) + validate_email(email) def test_domain_word(self): domain_word = self.factory.domain_word() @@ -155,7 +155,7 @@ def test_email(self): email = self.factory.email() - validate_email(email, check_deliverability=False) + validate_email(email) class TestHuHU(unittest.TestCase): @@ -200,7 +200,7 @@ ) def test_ascii_safe_email(self): email = self.factory.ascii_safe_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'fabienne' @mock.patch( @@ -209,7 +209,7 @@ ) def test_ascii_free_email(self): email = self.factory.ascii_free_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'fabienne' @mock.patch( @@ -218,7 +218,7 @@ ) def test_ascii_company_email(self): email = self.factory.ascii_company_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'fabienne' @@ -234,7 +234,7 @@ ) def test_ascii_safe_email(self): email = self.factory.ascii_safe_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'asyl' @mock.patch( @@ -243,7 +243,7 @@ ) def test_ascii_free_email(self): email = self.factory.ascii_free_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'asyl' @mock.patch( @@ -252,7 +252,7 @@ ) def test_ascii_company_email(self): email = self.factory.ascii_company_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'asyl' @@ -268,7 +268,7 @@ ) def test_ascii_safe_email(self): email = self.factory.ascii_safe_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'vitoriamagalhaes' @mock.patch( @@ -277,7 +277,7 @@ ) def test_ascii_free_email(self): email = self.factory.ascii_free_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'joaosimoes' @mock.patch( @@ -286,5 +286,5 @@ ) def test_ascii_company_email(self): email = self.factory.ascii_company_email() - validate_email(email, check_deliverability=False) + validate_email(email) assert email.split('@')[0] == 'andrecaua' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/tests/providers/test_person.py new/Faker-2.0.0/tests/providers/test_person.py --- old/Faker-1.0.7/tests/providers/test_person.py 2019-05-14 17:28:28.000000000 +0200 +++ new/Faker-2.0.0/tests/providers/test_person.py 2019-07-15 16:35:50.000000000 +0200 @@ -4,9 +4,14 @@ import re import unittest - +import datetime import six +try: + from unittest import mock +except ImportError: + import mock + from faker import Faker from faker.providers.person.ar_AA import Provider as ArProvider from faker.providers.person.fi_FI import Provider as FiProvider @@ -14,9 +19,9 @@ from faker.providers.person.ne_NP import Provider as NeProvider from faker.providers.person.sv_SE import Provider as SvSEProvider from faker.providers.person.cs_CZ import Provider as CsCZProvider +from faker.providers.person.pl_PL import Provider as PlPLProvider from faker.providers.person.pl_PL import ( checksum_identity_card_number as pl_checksum_identity_card_number, - checksum_pesel_number as pl_checksum_pesel_number, ) from faker.providers.person.zh_CN import Provider as ZhCNProvider from faker.providers.person.zh_TW import Provider as ZhTWProvider @@ -207,13 +212,43 @@ for _ in range(100): assert re.search(r'^[A-Z]{3}\d{6}$', self.factory.identity_card_number()) - def test_pesel_number_checksum(self): - assert pl_checksum_pesel_number('31090655159') is True - assert pl_checksum_pesel_number('95030853577') is True - assert pl_checksum_pesel_number('05260953442') is True - assert pl_checksum_pesel_number('31090655158') is False - assert pl_checksum_pesel_number('95030853576') is False - assert pl_checksum_pesel_number('05260953441') is False + @mock.patch.object(PlPLProvider, 'random_digit') + def test_pesel_birth_date(self, mock_random_digit): + mock_random_digit.side_effect = [3, 5, 8, 8, 7, 9, 9, 3] + assert self.factory.pesel(datetime.date(1999, 12, 31)) == '99123135885' + assert self.factory.pesel(datetime.date(2000, 1, 1)) == '00210179936' + + @mock.patch.object(PlPLProvider, 'random_digit') + def test_pesel_sex_male(self, mock_random_digit): + mock_random_digit.side_effect = [1, 3, 4, 5, 6, 1, 7, 0] + assert self.factory.pesel(datetime.date(1909, 3, 3), 'M') == '09030313454' + assert self.factory.pesel(datetime.date(1913, 8, 16), 'M') == '13081661718' + + @mock.patch.object(PlPLProvider, 'random_digit') + def test_pesel_sex_female(self, mock_random_digit): + mock_random_digit.side_effect = [4, 9, 1, 6, 6, 1, 7, 3] + assert self.factory.pesel(datetime.date(2007, 4, 13), 'F') == '07241349161' + assert self.factory.pesel(datetime.date(1933, 12, 16), 'F') == '33121661744' + + @mock.patch.object(PlPLProvider, 'random_digit') + def test_pwz_doctor(self, mock_random_digit): + mock_random_digit.side_effect = [6, 9, 1, 9, 6, 5, 2, 7, 9, 9, 1, 5] + assert self.factory.pwz_doctor() == '2691965' + assert self.factory.pwz_doctor() == '4279915' + + @mock.patch.object(PlPLProvider, 'random_digit') + def test_pwz_doctor_check_digit_zero(self, mock_random_digit): + mock_random_digit.side_effect = [0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 9, 9] + assert self.factory.pwz_doctor() == '6000012' + assert self.factory.pwz_doctor() == '1000090' + + @mock.patch.object(PlPLProvider, 'random_int') + @mock.patch.object(PlPLProvider, 'random_digit') + def test_pwz_nurse(self, mock_random_digit, mock_random_int): + mock_random_digit.side_effect = [3, 4, 5, 6, 7, 1, 7, 5, 1, 2] + mock_random_int.side_effect = [45, 3] + assert self.factory.pwz_nurse(kind='nurse') == '4534567P' + assert self.factory.pwz_nurse(kind='midwife') == '0317512A' class TestCsCZ(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/tests/providers/test_python.py new/Faker-2.0.0/tests/providers/test_python.py --- old/Faker-1.0.7/tests/providers/test_python.py 2019-04-15 17:10:34.000000000 +0200 +++ new/Faker-2.0.0/tests/providers/test_python.py 2019-07-12 17:17:07.000000000 +0200 @@ -20,16 +20,16 @@ self.assertEqual(0, random_int % 2) def test_pyint_bound_0(self): - self.assertEqual(0, self.factory.pyint(min=0, max=0)) + self.assertEqual(0, self.factory.pyint(min_value=0, max_value=0)) def test_pyint_bound_positive(self): - self.assertEqual(5, self.factory.pyint(min=5, max=5)) + self.assertEqual(5, self.factory.pyint(min_value=5, max_value=5)) def test_pyint_bound_negative(self): - self.assertEqual(-5, self.factory.pyint(min=-5, max=-5)) + self.assertEqual(-5, self.factory.pyint(min_value=-5, max_value=-5)) def test_pyint_range(self): - self.assertTrue(0 <= self.factory.pyint(min=0, max=2) <= 2) + self.assertTrue(0 <= self.factory.pyint(min_value=0, max_value=2) <= 2) class TestPyfloat(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/tests/providers/test_ssn.py new/Faker-2.0.0/tests/providers/test_ssn.py --- old/Faker-1.0.7/tests/providers/test_ssn.py 2019-05-08 18:02:39.000000000 +0200 +++ new/Faker-2.0.0/tests/providers/test_ssn.py 2019-07-12 17:17:07.000000000 +0200 @@ -9,6 +9,7 @@ import freezegun import pytest import random2 +from validators.i18n.es import es_cif as is_cif, es_nif as is_nif, es_nie as is_nie from faker import Faker from faker.providers.ssn.en_CA import checksum as ca_checksum @@ -18,6 +19,8 @@ from faker.providers.ssn.no_NO import checksum as no_checksum, Provider as no_Provider from faker.providers.ssn.pl_PL import checksum as pl_checksum, calculate_month as pl_calculate_mouth from faker.providers.ssn.pt_BR import checksum as pt_checksum +from faker.providers.ssn.es_MX import (ssn_checksum as mx_ssn_checksum, + curp_checksum as mx_curp_checksum) class TestBgBG(unittest.TestCase): @@ -113,6 +116,98 @@ assert 1 <= int(serial) <= 9999 assert area != '666' + def test_invalid_ssn(self): + self.factory.random = random2.Random() + # Magic Numbers below generate '666-92-7944', '000-54-2963', '956-GG-9478', '436-00-1386', + # and 134-76-0000 respectively. The "group" (GG) returned for '956-GG-9478 will be a random + # number, and that random number is not in the "itin_group_numbers" List. The random GG occurs + # even when using the same seed_instance() due to using random.choice() for GG to avoid valid + # ITINs being returned as an invalid SSN: + # + # Ensure that generated SSNs are 11 characters long + # including dashes, consist of dashes and digits only, and the tested number + # violates the requirements below, ensuring an INVALID SSN is returned: + # + # A United States Social Security Number + # (SSN) is a tax processing number issued by the Internal + # Revenue Service with the format "AAA-GG-SSSS". The + # number is divided into three parts: the first three + # digits, known as the area number because they were + # formerly assigned by geographical region; the middle two + # digits, known as the group number; and the final four + # digits, known as the serial number. SSNs with the + # following characteristics are not allocated: + # + # 1) Numbers with all zeros in any digit group + # (000-##-####, ###-00-####, ###-##-0000). + # + # 2) Numbers with 666 or 900-999 in the first digit group. + # + # https://en.wikipedia.org/wiki/Social_Security_number + # + # ITIN explained: + # https://www.irs.gov/individuals/international-taxpayers/general-itin-information + + itin_group_numbers = [ + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 90, + 91, + 92, + 94, + 95, + 96, + 97, + 98, + 99] + + self.factory.seed_instance(1143) + ssn = self.factory.ssn(taxpayer_identification_number_type='INVALID_SSN') + + assert len(ssn) == 11 + assert ssn.replace('-', '').isdigit() + assert ssn.startswith('666') + + self.factory.seed_instance(1514) + ssn = self.factory.ssn(taxpayer_identification_number_type='INVALID_SSN') + + assert ssn.startswith('000') + + self.factory.seed_instance(2) + ssn = self.factory.ssn(taxpayer_identification_number_type='INVALID_SSN') + [area, group, serial] = ssn.split('-') + + assert 900 <= int(area) <= 999 and int(group) not in itin_group_numbers + + self.factory.seed_instance(9) + ssn = self.factory.ssn(taxpayer_identification_number_type='INVALID_SSN') + [area, group, serial] = ssn.split('-') + + assert int(area) < 900 and int(group) == 0 + + self.factory.seed_instance(1) + ssn = self.factory.ssn(taxpayer_identification_number_type='INVALID_SSN') + [area, group, serial] = ssn.split('-') + + assert int(area) < 900 and int(serial) == 0 + def test_prohibited_ssn_value(self): # 666 is a prohibited value. The magic number selected as a seed # is one that would (if not specifically checked for) return an @@ -268,92 +363,6 @@ self.factory.ssn(taxpayer_identification_number_type='ssn') -def nif_nie_validation(doi, number_by_letter, special_cases): - """ - Validate if the doi is a NIF or a NIE. - :param doi: DOI to validate. - :return: boolean if it's valid. - """ - doi = doi.upper() - if doi in special_cases: - return False - - table = 'TRWAGMYFPDXBNJZSQVHLCKE' - - if len(doi) == 9: - control = doi[8] - - # If it is not a DNI, convert the first letter to the corresponding digit - numbers = number_by_letter.get(doi[0], doi[0]) + doi[1:8] - - return numbers.isdigit() and control == table[int(numbers) % 23] - - return False - - -def is_cif(doi): - """ - Validate if the doi is a CIF. - :param doi: DOI to validate. - :return: boolean if it's valid. - """ - doi = doi.upper() - - if len(doi) != 9: - return False - - table = 'JABCDEFGHI' - first_chr = doi[0] - doi_body = doi[1:8] - control = doi[8] - - if not doi_body.isdigit(): - return False - - # Multiply each each odd position doi digit by 2 and sum it all together - odd_result = sum(int(x) for x in ''.join(str(int(x) * 2) for x in doi_body[0::2])) - # Sum all even doi digits - even_result = sum(map(int, doi_body[1::2])) - - res = (10 - (even_result + odd_result) % 10) % 10 - - if first_chr in 'ABEH': # Number type - return str(res) == control - elif first_chr in 'PSQW': # Letter type - return table[res] == control - elif first_chr not in 'CDFGJNRUV': - return False - - return control == str(res) or control == table[res] - - -def is_nif(doi): - """ - Validate if the doi is a NIF. - :param doi: DOI to validate. - :return: boolean if it's valid. - """ - number_by_letter = {'L': '0', 'M': '0', 'K': '0'} - special_cases = ['X0000000T', '00000000T', '00000001R', '00000001R'] - return nif_nie_validation(doi, number_by_letter, special_cases) - - -def is_nie(doi): - """ - Validate if the doi is a NIE. - :param doi: DOI to validate. - :return: boolean if it's valid. - """ - number_by_letter = {'X': '0', 'Y': '1', 'Z': '2'} - special_cases = ['X0000000T'] - - # NIE must must start with X Y or Z - if not doi or doi[0] not in number_by_letter.keys(): - return False - - return nif_nie_validation(doi, number_by_letter, special_cases) - - class TestEsES(unittest.TestCase): def setUp(self): self.factory = Faker('es_ES') @@ -383,6 +392,41 @@ self.factory = Faker('es_CA') +class TestEsMX(unittest.TestCase): + def setUp(self): + self.factory = Faker('es_MX') + + def test_ssn(self): + for _ in range(100): + ssn = self.factory.ssn() + + assert len(ssn) == 11 + assert ssn.isnumeric() + assert mx_ssn_checksum(map(int, ssn[:-1])) == int(ssn[-1]) + + def test_curp(self): + for _ in range(100): + curp = self.factory.curp() + + assert len(curp) == 18 + assert re.search(r'^[A-Z]{4}\d{6}[A-Z]{6}[0A]\d$', curp) + assert mx_curp_checksum(curp[:-1]) == int(curp[-1]) + + def test_rfc_natural(self): + for _ in range(100): + rfc = self.factory.rfc() + + assert len(rfc) == 13 + assert re.search(r'^[A-Z]{4}\d{6}[0-9A-Z]{3}$', rfc) + + def test_rfc_legal(self): + for _ in range(100): + rfc = self.factory.rfc(natural=False) + + assert len(rfc) == 12 + assert re.search(r'^[A-Z]{3}\d{6}[0-9A-Z]{3}$', rfc) + + class TestEtEE(unittest.TestCase): """ Tests SSN in the et_EE locale """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Faker-1.0.7/tests/test_factory.py new/Faker-2.0.0/tests/test_factory.py --- old/Faker-1.0.7/tests/test_factory.py 2019-05-14 17:28:28.000000000 +0200 +++ new/Faker-2.0.0/tests/test_factory.py 2019-07-15 16:39:47.000000000 +0200 @@ -6,6 +6,8 @@ import unittest import string import sys + +from collections import OrderedDict from ipaddress import ip_address, ip_network import six @@ -218,13 +220,17 @@ pick = provider.random_element(choices) assert pick in choices - choices = {'a': 5, 'b': 2, 'c': 2, 'd': 1} + # dicts not allowed because they introduce dependency on PYTHONHASHSEED + with self.assertRaises(ValueError): + provider.random_element({}) + + choices = OrderedDict([('a', 5), ('b', 2), ('c', 2), ('d', 1)]) pick = provider.random_element(choices) - assert pick in choices + self.assertTrue(pick in choices) - choices = {'a': 0.5, 'b': 0.2, 'c': 0.2, 'd': 0.1} + choices = OrderedDict([('a', 0.5), ('b', 0.2), ('c', 0.2), ('d', 0.1)]) pick = provider.random_element(choices) - assert pick in choices + self.assertTrue(pick in choices) def test_binary(self): from faker.providers.misc import Provider
