From b8131ffbfb5da3eb746862ae9ea59e46769d1959 Mon Sep 17 00:00:00 2001 From: Mauko Quiroga Date: Mon, 14 Oct 2024 16:20:13 +0200 Subject: [PATCH 1/4] build: normalize pyproject --- .flake8 | 20 ---- Makefile | 13 ++- openfisca_country_template/__init__.py | 24 ++-- openfisca_country_template/entities.py | 63 ++++++----- .../reforms/__init__.py | 6 +- .../reforms/add_dynamic_variable.py | 49 ++++---- .../reforms/add_new_tax.py | 14 +-- .../flat_social_security_contribution.py | 25 ++-- .../modify_social_security_taxation.py | 32 +++--- .../reforms/removal_basic_income.py | 19 ++-- .../situation_examples/__init__.py | 3 +- .../variables/__init__.py | 3 +- .../variables/benefits.py | 76 ++++++++----- .../variables/demographics.py | 20 ++-- .../variables/housing.py | 10 +- .../variables/income.py | 17 +-- openfisca_country_template/variables/stats.py | 26 +++-- openfisca_country_template/variables/taxes.py | 43 ++++--- pyproject.toml | 107 ++++++++++-------- 19 files changed, 303 insertions(+), 267 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index cd91d602..00000000 --- a/.flake8 +++ /dev/null @@ -1,20 +0,0 @@ -[flake8] -hang-closing = true -ignore = D101,D102,D103,D107,D401,E128,E251,E501,W503 -in-place = true -inline-quotes = " -multiline-quotes = """ -import-order-style = appnexus -no-accept-encodings = true -application-import-names = openfisca_country_template -application-package-names = openfisca_core -exclude = .git,__pycache__,.venv,.github,.devcontainer,docs - -; D101: Variables already provide label/description -; D102/103: Do not document methods/functions -; D107: Do not document __init__ method -; D401: Do not require the imperative mood -; E128/133: Prefer hang-closing visual indents -; E251: Prefer `function(x = 1)` over `function(x=1)` -; E501: Do not enforce a maximum line length -; W503/4: Break lines before binary operators (Knuth's style) diff --git a/Makefile b/Makefile index 9e57fd6e..978269a2 100644 --- a/Makefile +++ b/Makefile @@ -14,13 +14,14 @@ install: deps @# Install OpenFisca-Extension-Template for development. @# `make install` installs the editable version of openfisca-country_template. @# This allows contributors to test as they code. - pip install --editable .[dev] --upgrade --use-deprecated=legacy-resolver + pip install --editable .[dev] --upgrade build: clean deps @# Install OpenFisca-Extension-Template for deployment and publishing. @# `make build` allows us to be be sure tests are run against the packaged version @# of OpenFisca-Extension-Template, the same we put in the hands of users and reusers. python -m build + pip uninstall --yes openfisca-country-template find dist -name "*.whl" -exec pip install --force-reinstall {}[dev] \; check-syntax-errors: @@ -29,16 +30,16 @@ check-syntax-errors: format-style: @# Do not analyse .gitignored files. @# `make` needs `$$` to output `$`. Ref: http://stackoverflow.com/questions/2382764. + ruff format `git ls-files | grep "\.py$$"` isort `git ls-files | grep "\.py$$"` - autopep8 `git ls-files | grep "\.py$$"` - pyupgrade --py39-plus `git ls-files | grep "\.py$$"` + black `git ls-files | grep "\.py$$"` check-style: @# Do not analyse .gitignored files. @# `make` needs `$$` to output `$`. Ref: http://stackoverflow.com/questions/2382764. - flake8 `git ls-files | grep "\.py$$"` - pylint `git ls-files | grep "\.py$$"` - yamllint `git ls-files | grep "\.yaml$$"` + ruff check `git ls-files | grep "\.py$$"` + isort --check `git ls-files | grep "\.py$$"` + black --check `git ls-files | grep "\.py$$"` test: clean check-syntax-errors check-style openfisca test --country-package openfisca_country_template openfisca_country_template/tests diff --git a/openfisca_country_template/__init__.py b/openfisca_country_template/__init__.py index a1ca8a92..d8fc243b 100644 --- a/openfisca_country_template/__init__.py +++ b/openfisca_country_template/__init__.py @@ -1,9 +1,11 @@ -""" -This file defines our country's tax and benefit system. +"""This file defines our country's tax and benefit system. A tax and benefit system is the higher-level instance in OpenFisca. + Its goal is to model the legislation of a country. -Basically a tax and benefit system contains simulation variables (source code) and legislation parameters (data). + +Basically a tax and benefit system contains simulation variables (source code) +and legislation parameters (data). See https://openfisca.org/doc/key-concepts/tax_and_benefit_system.html """ @@ -15,27 +17,31 @@ from openfisca_country_template import entities from openfisca_country_template.situation_examples import couple - COUNTRY_DIR = os.path.dirname(os.path.abspath(__file__)) -# Our country tax and benefit class inherits from the general TaxBenefitSystem class. -# The name CountryTaxBenefitSystem must not be changed, as all tools of the OpenFisca ecosystem expect a CountryTaxBenefitSystem class to be exposed in the __init__ module of a country package. +# Our country tax and benefit class inherits from the general TaxBenefitSystem +# class. The name CountryTaxBenefitSystem must not be changed, as all tools of +# the OpenFisca ecosystem expect a CountryTaxBenefitSystem class to be exposed +# in the __init__ module of a country package. class CountryTaxBenefitSystem(TaxBenefitSystem): def __init__(self): + """Initialize our country's tax and benefit system.""" # We initialize our tax and benefit system with the general constructor super().__init__(entities.entities) # We add to our tax and benefit system all the variables self.add_variables_from_directory(os.path.join(COUNTRY_DIR, "variables")) - # We add to our tax and benefit system all the legislation parameters defined in the parameters files + # We add to our tax and benefit system all the legislation parameters + # defined in the parameters files param_path = os.path.join(COUNTRY_DIR, "parameters") self.load_parameters(param_path) - # We define which variable, parameter and simulation example will be used in the OpenAPI specification + # We define which variable, parameter and simulation example will be + # used in the OpenAPI specification self.open_api_config = { "variable_example": "disposable_income", "parameter_example": "taxes.income_tax_rate", "simulation_example": couple, - } + } diff --git a/openfisca_country_template/entities.py b/openfisca_country_template/entities.py index 297bed6a..2cd3c255 100644 --- a/openfisca_country_template/entities.py +++ b/openfisca_country_template/entities.py @@ -1,7 +1,7 @@ -""" -This file defines the entities needed by our legislation. +"""This file defines the entities needed by our legislation. -Taxes and benefits can be calculated for different entities: persons, household, companies, etc. +Taxes and benefits can be calculated for different entities: persons, household, +companies, etc. See https://openfisca.org/doc/key-concepts/person,_entities,_role.html """ @@ -9,24 +9,32 @@ from openfisca_core.entities import build_entity Household = build_entity( - key = "household", - plural = "households", - label = "All the people in a family or group who live together in the same place.", - doc = """ + key="household", + plural="households", + label="All the people in a family or group who live together in the same place.", + doc=""" Household is an example of a group entity. A group entity contains one or more individual·s. - Each individual in a group entity has a role (e.g. parent or children). Some roles can only be held by a limited number of individuals (e.g. a 'first_parent' can only be held by one individual), while others can have an unlimited number of individuals (e.g. 'children'). + Each individual in a group entity has a role (e.g. parent or children). + Some roles can only be held by a limited number of individuals (e.g. a + 'first_parent' can only be held by one individual), while others can + have an unlimited number of individuals (e.g. 'children'). Example: - Housing variables (e.g. housing_tax') are usually defined for a group entity such as 'Household'. + Housing variables (e.g. housing_tax') are usually defined for a group + entity such as 'Household'. Usage: - Check the number of individuals of a specific role (e.g. check if there is a 'second_parent' with household.nb_persons(Household.SECOND_PARENT)). - Calculate a variable applied to each individual of the group entity (e.g. calculate the 'salary' of each member of the 'Household' with salaries = household.members("salary", period = MONTH); sum_salaries = household.sum(salaries)). + Check the number of individuals of a specific role (e.g. check if there + is a 'second_parent' with household.nb_persons(Household.SECOND_PARENT)). + Calculate a variable applied to each individual of the group entity + (e.g. calculate the 'salary' of each member of the 'Household' with: + salaries = household.members("salary", period = MONTH) + sum_salaries = household.sum(salaries)). For more information, see: https://openfisca.org/doc/coding-the-legislation/50_entities.html """, - roles = [ + roles=[ { "key": "parent", "plural": "parents", @@ -34,31 +42,34 @@ "max": 2, "subroles": ["first_parent", "second_parent"], "doc": "The one or two adults in charge of the household.", - }, + }, { "key": "child", "plural": "children", "label": "Child", "doc": "Other individuals living in the household.", - }, - ], - ) + }, + ], +) Person = build_entity( - key = "person", - plural = "persons", - label = "An individual. The minimal legal entity on which a legislation might be applied.", - doc = """ - - Variables like 'salary' and 'income_tax' are usually defined for the entity 'Person'. + key="person", + plural="persons", + label="An individual. The minimal entity on which legislation can be applied.", + doc=""" + Variables like 'salary' and 'income_tax' are usually defined for the entity + 'Person'. Usage: - Calculate a variable applied to a 'Person' (e.g. access the 'salary' of a specific month with person("salary", "2017-05")). - Check the role of a 'Person' in a group entity (e.g. check if a the 'Person' is a 'first_parent' in a 'Household' entity with person.has_role(Household.FIRST_PARENT)). + Calculate a variable applied to a 'Person' (e.g. access the 'salary' of + a specific month with person("salary", "2017-05")). + Check the role of a 'Person' in a group entity (e.g. check if a the + 'Person' is a 'first_parent' in a 'Household' entity with + person.has_role(Household.FIRST_PARENT)). For more information, see: https://openfisca.org/doc/coding-the-legislation/50_entities.html """, - is_person = True, - ) + is_person=True, +) entities = [Household, Person] diff --git a/openfisca_country_template/reforms/__init__.py b/openfisca_country_template/reforms/__init__.py index bde04d0a..ee4e01ea 100644 --- a/openfisca_country_template/reforms/__init__.py +++ b/openfisca_country_template/reforms/__init__.py @@ -1,7 +1,7 @@ -""" -This sub-package is used to define reforms. +"""This sub-package is used to define reforms. -A reform is a set of modifications to be applied to a reference tax and benefit system to carry out experiments. +A reform is a set of modifications to be applied to a reference tax and benefit +system to carry out experiments. See https://openfisca.org/doc/key-concepts/reforms.html """ diff --git a/openfisca_country_template/reforms/add_dynamic_variable.py b/openfisca_country_template/reforms/add_dynamic_variable.py index 84718662..60d28414 100644 --- a/openfisca_country_template/reforms/add_dynamic_variable.py +++ b/openfisca_country_template/reforms/add_dynamic_variable.py @@ -1,12 +1,12 @@ -""" -This file defines a reform to add a dynamic variable, based on input data. +"""This file defines a reform to add a dynamic variable, based on input data. -A reform is a set of modifications to be applied to a reference tax and benefit system to carry out experiments. +A reform is a set of modifications to be applied to a reference tax and benefit +system to carry out experiments. See https://openfisca.org/doc/key-concepts/reforms.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.periods import MONTH from openfisca_core.reforms import Reform from openfisca_core.variables import Variable @@ -17,22 +17,23 @@ def create_dynamic_variable(name, **variable): """Create new variable dynamically.""" - NewVariable = type(name, (Variable,), { - "value_type": variable["value_type"], - "entity": variable["entity"], - "default_value": variable["default_value"], - "definition_period": variable["definition_period"], - "label": variable["label"], - "reference": variable["reference"], - }) - - return NewVariable + return type( + name, + (Variable,), + { + "value_type": variable["value_type"], + "entity": variable["entity"], + "default_value": variable["default_value"], + "definition_period": variable["definition_period"], + "label": variable["label"], + "reference": variable["reference"], + }, + ) class add_dynamic_variable(Reform): def apply(self): - """ - Apply reform. + """Apply reform. A reform always defines an `apply` method that builds the reformed tax and benefit system from the reference one. @@ -40,13 +41,13 @@ def apply(self): See https://openfisca.org/doc/coding-the-legislation/reforms.html#writing-a-reform """ NewVariable = create_dynamic_variable( - name = "goes_to_school", - value_type = bool, - entity = Person, - default_value = True, - definition_period = MONTH, - label = "The person goes to school (only relevant for children)", - reference = "https://law.gov.example/goes_to_school", - ) + name="goes_to_school", + value_type=bool, + entity=Person, + default_value=True, + definition_period=MONTH, + label="The person goes to school (only relevant for children)", + reference="https://law.gov.example/goes_to_school", + ) self.add_variable(NewVariable) diff --git a/openfisca_country_template/reforms/add_new_tax.py b/openfisca_country_template/reforms/add_new_tax.py index 710698c3..b9a5ac54 100644 --- a/openfisca_country_template/reforms/add_new_tax.py +++ b/openfisca_country_template/reforms/add_new_tax.py @@ -1,12 +1,12 @@ -""" -This file defines a reform. +"""This file defines a reform. -A reform is a set of modifications to be applied to a reference tax and benefit system to carry out experiments. +A reform is a set of modifications to be applied to a reference tax and benefit +system to carry out experiments. See https://openfisca.org/doc/key-concepts/reforms.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.periods import MONTH from openfisca_core.reforms import Reform from openfisca_core.variables import Variable @@ -32,8 +32,7 @@ class new_tax(Variable): reference = "https://law.gov.example/new_tax" # Always use the most official source def formula(person, period, _parameters): - """ - New tax reform. + """New tax reform. Our reform adds a new variable `new_tax` that is calculated based on the current `income_tax`, if the person has a car. @@ -46,8 +45,7 @@ def formula(person, period, _parameters): class add_new_tax(Reform): def apply(self): - """ - Apply reform. + """Apply reform. A reform always defines an `apply` method that builds the reformed tax and benefit system from the reference one. diff --git a/openfisca_country_template/reforms/flat_social_security_contribution.py b/openfisca_country_template/reforms/flat_social_security_contribution.py index c224fc1c..dddc9a44 100644 --- a/openfisca_country_template/reforms/flat_social_security_contribution.py +++ b/openfisca_country_template/reforms/flat_social_security_contribution.py @@ -1,34 +1,35 @@ -""" -This file defines a reform. +"""This file defines a reform. -A reform is a set of modifications to be applied to a reference tax and benefit system to carry out experiments. +A reform is a set of modifications to be applied to a reference tax and benefit +system to carry out experiments. See https://openfisca.org/doc/key-concepts/reforms.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.reforms import Reform from openfisca_core.variables import Variable class social_security_contribution(Variable): - # Variable metadata don't need to be redefined. By default, the reference variable metadatas will be used. + # Variable metadata don't need to be redefined. By default, the reference + # variable metadata will be used. def formula(person, period, _parameters): - """ - Social security contribution reform. + """Social security contribution reform. - Our reform replaces `social_security_contribution` (the "reference" variable) by the following variable. + Our reform replaces `social_security_contribution` (the "reference" + variable) by the following variable. """ return person("salary", period) * 0.03 class flat_social_security_contribution(Reform): def apply(self): - """ - Apply reform. + """Apply reform. - A reform always defines an `apply` method that builds the reformed tax and benefit system from the reference one. - See https://openfisca.org/doc/coding-the-legislation/reforms.html#writing-a-reform + A reform always defines an `apply` method that builds the reformed tax + and benefit system from the reference one. See + https://openfisca.org/doc/coding-the-legislation/reforms.html#writing-a-reform """ self.update_variable(social_security_contribution) diff --git a/openfisca_country_template/reforms/modify_social_security_taxation.py b/openfisca_country_template/reforms/modify_social_security_taxation.py index 7d4099d7..6e88c123 100644 --- a/openfisca_country_template/reforms/modify_social_security_taxation.py +++ b/openfisca_country_template/reforms/modify_social_security_taxation.py @@ -1,35 +1,35 @@ -""" -This file defines a reform. +"""This file defines a reform. -A reform is a set of modifications to be applied to a reference tax and benefit system to carry out experiments. +A reform is a set of modifications to be applied to a reference tax and benefit +system to carry out experiments. See https://openfisca.org/doc/key-concepts/reforms.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.parameters import ParameterScaleBracket from openfisca_core.reforms import Reform class modify_social_security_taxation(Reform): def apply(self): - """ - Apply reform. + """Apply reform. - A reform always defines an `apply` method that builds the reformed tax and benefit system from the reference one. - See https://openfisca.org/doc/coding-the-legislation/reforms.html#writing-a-reform + A reform always defines an `apply` method that builds the reformed tax + and benefit system from the reference one. See + https://openfisca.org/doc/coding-the-legislation/reforms.html#writing-a-reform - Our reform modifies the `social_security_contribution` parameter, which is a scale. - This parameter is declared in `parameters/taxes/social_security_contribution.yaml`. + Our reform modifies the `social_security_contribution` parameter, which + is a scale. This parameter is declared in + `parameters/taxes/social_security_contribution.yaml`. See https://openfisca.org/doc/coding-the-legislation/legislation_parameters.html """ - self.modify_parameters(modifier_function = self.modify_brackets) + self.modify_parameters(modifier_function=self.modify_brackets) @staticmethod def modify_brackets(parameters): - """ - Social security taxation reform. + """Social security taxation reform. This function takes an argument `parameters` which is a in-memory representation of the YAML parameters. It can be modified and must be returned. @@ -47,11 +47,11 @@ def modify_brackets(parameters): # Add a new bracket with a higher tax rate for rich people: new_bracket = ParameterScaleBracket( "new_bracket", - data = { + data={ "rate": {"2017-01-01": {"value": 0.4}}, "threshold": {"2017-01-01": {"value": 40000}}, - }, - ) + }, + ) brackets.append(new_bracket) return parameters diff --git a/openfisca_country_template/reforms/removal_basic_income.py b/openfisca_country_template/reforms/removal_basic_income.py index 58de4968..754e7558 100644 --- a/openfisca_country_template/reforms/removal_basic_income.py +++ b/openfisca_country_template/reforms/removal_basic_income.py @@ -1,23 +1,24 @@ -""" -This file defines a reform. +"""This file defines a reform. -A reform is a set of modifications to be applied to a reference tax and benefit system to carry out experiments. +A reform is a set of modifications to be applied to a reference tax and benefit +system to carry out experiments. See https://openfisca.org/doc/key-concepts/reforms.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.reforms import Reform class removal_basic_income(Reform): def apply(self): - """ - Apply reform. + """Apply reform. - A reform always defines an `apply` method that builds the reformed tax and benefit system from the reference one. - See https://openfisca.org/doc/coding-the-legislation/reforms.html#writing-a-reform + A reform always defines an `apply` method that builds the reformed tax + and benefit system from the reference one. See + https://openfisca.org/doc/coding-the-legislation/reforms.html#writing-a-reform - Our reform neutralizes the `basic_income` variable. When this reform is applied, calculating `basic_income` will always return its default value, 0. + Our reform neutralizes the `basic_income` variable. When this reform is + applied, calculating `basic_income` will always return its default value, 0. """ self.neutralize_variable("basic_income") diff --git a/openfisca_country_template/situation_examples/__init__.py b/openfisca_country_template/situation_examples/__init__.py index b1b7cfb4..27ef68f7 100644 --- a/openfisca_country_template/situation_examples/__init__.py +++ b/openfisca_country_template/situation_examples/__init__.py @@ -3,14 +3,13 @@ import json import os - DIR_PATH = os.path.dirname(os.path.abspath(__file__)) def parse(file_name): """Load json example situations.""" file_path = os.path.join(DIR_PATH, file_name) - with open(file_path, "r", encoding="utf8") as file: + with open(file_path, encoding="utf8") as file: return json.loads(file.read()) diff --git a/openfisca_country_template/variables/__init__.py b/openfisca_country_template/variables/__init__.py index 718ad20d..dd8e3f45 100644 --- a/openfisca_country_template/variables/__init__.py +++ b/openfisca_country_template/variables/__init__.py @@ -1,5 +1,4 @@ -""" -This sub-package is used to define variables. +"""This sub-package is used to define variables. A variable is a property of an Entity such as a Person, a Household… diff --git a/openfisca_country_template/variables/benefits.py b/openfisca_country_template/variables/benefits.py index 3703113d..13319be4 100644 --- a/openfisca_country_template/variables/benefits.py +++ b/openfisca_country_template/variables/benefits.py @@ -1,12 +1,11 @@ -""" -This file defines variables for the modelled legislation. +"""This file defines variables for the modelled legislation. A variable is a property of an Entity such as a Person, a Household… See https://openfisca.org/doc/key-concepts/variables.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.periods import MONTH from openfisca_core.variables import Variable @@ -19,27 +18,39 @@ class basic_income(Variable): entity = Person definition_period = MONTH label = "Basic income provided to adults" - reference = "https://law.gov.example/basic_income" # Always use the most official source + reference = ( + "https://law.gov.example/basic_income" # Always use the most official source + ) def formula_2016_12(person, period, parameters): - """ - Basic income provided to adults. + """Basic income provided to adults. - Since Dec 1st 2016, the basic income is provided to any adult, without considering their income. + Since Dec 1st 2016, the basic income is provided to any adult, without + considering their income. """ - age_condition = person("age", period) >= parameters(period).general.age_of_majority - return age_condition * parameters(period).benefits.basic_income # This '*' is a vectorial 'if'. See https://openfisca.org/doc/coding-the-legislation/25_vectorial_computing.html#control-structures + age_condition = ( + person("age", period) >= parameters(period).general.age_of_majority + ) + # This '*' is a vectorial 'if'. See + # https://openfisca.org/doc/coding-the-legislation/25_vectorial_computing.html#control-structures + return age_condition * parameters(period).benefits.basic_income def formula_2015_12(person, period, parameters): - """ - Basic income provided to adults. + """Basic income provided to adults. - From Dec 1st 2015 to Nov 30 2016, the basic income is provided to adults who have no income. - Before Dec 1st 2015, the basic income does not exist in the law, and calculating it returns its default value, which is 0. + From Dec 1st 2015 to Nov 30 2016, the basic income is provided to adults + who have no income. Before Dec 1st 2015, the basic income does not exist + in the law, and calculating it returns its default value, which is 0. """ - age_condition = person("age", period) >= parameters(period).general.age_of_majority + age_condition = ( + person("age", period) >= parameters(period).general.age_of_majority + ) salary_condition = person("salary", period) == 0 - return age_condition * salary_condition * parameters(period).benefits.basic_income # The '*' is also used as a vectorial 'and'. See https://openfisca.org/doc/coding-the-legislation/25_vectorial_computing.html#boolean-operations + # The '*' is also used as a vectorial 'and'. See + # https://openfisca.org/doc/coding-the-legislation/25_vectorial_computing.html#boolean-operations + return ( + age_condition * salary_condition * parameters(period).benefits.basic_income + ) class housing_allowance(Variable): @@ -47,8 +58,11 @@ class housing_allowance(Variable): entity = Household definition_period = MONTH label = "Housing allowance" - reference = "https://law.gov.example/housing_allowance" # Always use the most official source - end = "2016-11-30" # This allowance was removed on the 1st of Dec 2016. Calculating it before this date will always return the variable default value, 0. + # Always use the most official source + reference = "https://law.gov.example/housing_allowance" + # This allowance was removed on the 1st of Dec 2016. Calculating it before + # this date will always return the variable default value, 0. + end = "2016-11-30" unit = "currency-EUR" documentation = """ This allowance was introduced on the 1st of Jan 1980. @@ -56,36 +70,37 @@ class housing_allowance(Variable): """ def formula_1980(household, period, parameters): - """ - Housing allowance. + """Housing allowance. - This allowance was introduced on the 1st of Jan 1980. - Calculating it before this date will always return the variable default value, 0. + This allowance was introduced on the 1st of Jan 1980. Calculating it + before this date will always return the variable default value, 0. - To compute this allowance, the 'rent' value must be provided for the same month, - but 'housing_occupancy_status' is not necessary. + To compute this allowance, the 'rent' value must be provided for the + same month, but 'housing_occupancy_status' is not necessary. """ return household("rent", period) * parameters(period).benefits.housing_allowance -# By default, you can use utf-8 characters in a variable. OpenFisca web API manages utf-8 encoding. +# By default, you can use utf-8 characters in a variable. OpenFisca web API +# manages utf-8 encoding. class pension(Variable): value_type = float entity = Person definition_period = MONTH label = "Pension for the elderly. Pension attribuée aux personnes âgées. تقاعد." - reference = ["https://fr.wikipedia.org/wiki/Retraite_(économie)", "https://ar.wikipedia.org/wiki/تقاعد"] + reference = ( + "https://fr.wikipedia.org/wiki/Retraite_(économie)", + "https://ar.wikipedia.org/wiki/تقاعد", + ) def formula(person, period, parameters): - """ - Pension for the elderly. + """Pension for the elderly. A person's pension depends on their birth date. In French: retraite selon l'âge. In Arabic: تقاعد. """ - age_condition = person("age", period) >= parameters(period).general.age_of_retirement - return age_condition + return person("age", period) >= parameters(period).general.age_of_retirement class parenting_allowance(Variable): @@ -97,8 +112,7 @@ class parenting_allowance(Variable): reference = "https://www.servicesaustralia.gov.au/individuals/services/centrelink/parenting-payment/who-can-get-it" def formula(household, period, parameters): - """ - Parenting allowance for households. + """Parenting allowance for households. A person's parenting allowance depends on how many dependents they have, how much they, and their partner, earn diff --git a/openfisca_country_template/variables/demographics.py b/openfisca_country_template/variables/demographics.py index e2872a99..406d9e47 100644 --- a/openfisca_country_template/variables/demographics.py +++ b/openfisca_country_template/variables/demographics.py @@ -1,5 +1,4 @@ -""" -This file defines variables for the modelled legislation. +"""This file defines variables for the modelled legislation. A variable is a property of an Entity such as a Person, a Household… @@ -9,7 +8,7 @@ from datetime import date # Import from numpy the operations you need to apply on OpenFisca's population vectors -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from numpy import where from openfisca_core.periods import ETERNITY, MONTH @@ -22,7 +21,9 @@ # This variable is a pure input: it doesn't have a formula class birth(Variable): value_type = date - default_value = date(1970, 1, 1) # By default, if no value is set for a simulation, we consider the people involved in a simulation to be born on the 1st of Jan 1970. + # By default, if no value is set for a simulation, we consider the people + # involved in a simulation to be born on the 1st of Jan 1970. + default_value = date(1970, 1, 1) entity = Person label = "Birth date" definition_period = ETERNITY # This variable cannot change over time. @@ -36,8 +37,7 @@ class age(Variable): label = "Person's age (in years)" def formula(person, period, _parameters): - """ - Person's age (in years). + """Person's age (in years). A person's age is computed according to its birth date. """ @@ -46,6 +46,10 @@ def formula(person, period, _parameters): birth_month = birth.astype("datetime64[M]").astype(int) % 12 + 1 birth_day = (birth - birth.astype("datetime64[M]") + 1).astype(int) - is_birthday_past = (birth_month < period.start.month) + (birth_month == period.start.month) * (birth_day <= period.start.day) + is_birthday_past = (birth_month < period.start.month) + ( + birth_month == period.start.month + ) * (birth_day <= period.start.day) - return (period.start.year - birth_year) - where(is_birthday_past, 0, 1) # If the birthday is not passed this year, subtract one year + return (period.start.year - birth_year) - where( + is_birthday_past, 0, 1 + ) # If the birthday is not passed this year, subtract one year diff --git a/openfisca_country_template/variables/housing.py b/openfisca_country_template/variables/housing.py index 780720e9..eec53fdf 100644 --- a/openfisca_country_template/variables/housing.py +++ b/openfisca_country_template/variables/housing.py @@ -1,12 +1,11 @@ -""" -This file defines variables for the modelled legislation. +"""This file defines variables for the modelled legislation. A variable is a property of an Entity such as a Person, a Household… See https://openfisca.org/doc/key-concepts/variables.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.indexed_enums import Enum from openfisca_core.periods import MONTH from openfisca_core.variables import Variable @@ -31,8 +30,9 @@ class rent(Variable): label = "Rent paid by the household" -# Possible values for the housing_occupancy_status variable, defined further down -# See more at +# Possible values for the housing_occupancy_status variable, defined further +# down. See more at +# class HousingOccupancyStatus(Enum): __order__ = "owner tenant free_lodger homeless" owner = "Owner" diff --git a/openfisca_country_template/variables/income.py b/openfisca_country_template/variables/income.py index 1b48144c..1479471b 100644 --- a/openfisca_country_template/variables/income.py +++ b/openfisca_country_template/variables/income.py @@ -1,12 +1,11 @@ -""" -This file defines variables for the modelled legislation. +"""This file defines variables for the modelled legislation. A variable is a property of an Entity such as a Person, a Household… See https://openfisca.org/doc/key-concepts/variables.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.holders import set_input_divide_by_period from openfisca_core.periods import MONTH from openfisca_core.variables import Variable @@ -20,7 +19,9 @@ class salary(Variable): value_type = float entity = Person definition_period = MONTH - set_input = set_input_divide_by_period # Optional attribute. Allows user to declare a salary for a year. OpenFisca will spread the yearly amount over the months contained in the year. + # Optional attribute. Allows user to declare a salary for a year. OpenFisca + # will spread the yearly amount over the months contained in the year. + set_input = set_input_divide_by_period label = "Salary" reference = "https://law.gov.example/salary" # Always use the most official source @@ -30,13 +31,15 @@ class disposable_income(Variable): entity = Person definition_period = MONTH label = "Actual amount available to the person at the end of the month" - reference = "https://stats.gov.example/disposable_income" # Some variables represent quantities used in economic models, and not defined by law. Always give the source of your definitions. + # Some variables represent quantities used in economic models, and not + # defined by law. Always give the source of your definitions. + reference = "https://stats.gov.example/disposable_income" def formula(person, period, _parameters): """Disposable income.""" return ( - + person("salary", period) + +person("salary", period) + person("basic_income", period) - person("income_tax", period) - person("social_security_contribution", period) - ) + ) diff --git a/openfisca_country_template/variables/stats.py b/openfisca_country_template/variables/stats.py index d2fa6f49..361f1c45 100644 --- a/openfisca_country_template/variables/stats.py +++ b/openfisca_country_template/variables/stats.py @@ -1,12 +1,11 @@ -""" -This file defines variables for the modelled legislation. +"""This file defines variables for the modelled legislation. A variable is a property of an Entity such as a Person, a Household… See https://openfisca.org/doc/key-concepts/variables.html """ -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from openfisca-core the objects used to code the legislation in OpenFisca from openfisca_core.periods import MONTH from openfisca_core.variables import Variable @@ -23,12 +22,15 @@ class total_benefits(Variable): def formula(household, period, _parameters): """Total benefits.""" - basic_income_i = household.members("basic_income", period) # Calculates the value of basic_income for each member of the household + basic_income_i = household.members( + "basic_income", period + ) # Calculates the value of basic_income for each member of the household - return ( - + household.sum(basic_income_i) # Sum the household members basic incomes - + household("housing_allowance", period) - ) + return +household.sum( + basic_income_i + ) + household( # Sum the household members basic incomes + "housing_allowance", period + ) class total_taxes(Variable): @@ -41,10 +43,12 @@ class total_taxes(Variable): def formula(household, period, _parameters): """Total taxes.""" income_tax_i = household.members("income_tax", period) - social_security_contribution_i = household.members("social_security_contribution", period) + social_security_contribution_i = household.members( + "social_security_contribution", period + ) return ( - + household.sum(income_tax_i) + +household.sum(income_tax_i) + household.sum(social_security_contribution_i) + household("housing_tax", period.this_year) / 12 - ) + ) diff --git a/openfisca_country_template/variables/taxes.py b/openfisca_country_template/variables/taxes.py index 5e93700e..b6dd6a6c 100644 --- a/openfisca_country_template/variables/taxes.py +++ b/openfisca_country_template/variables/taxes.py @@ -1,13 +1,12 @@ -""" -This file defines variables for the modelled legislation. +"""This file defines variables for the modelled legislation. A variable is a property of an Entity such as a Person, a Household… See https://openfisca.org/doc/key-concepts/variables.html """ -# Import from numpy the operations you need to apply on OpenFisca's population vectors -# Import from openfisca-core the Python objects used to code the legislation in OpenFisca +# Import from numpy the what you need to apply on OpenFisca's population vectors +# Import from openfisca-core the objects used to code the legislation in OpenFisca from numpy import maximum as max_ from openfisca_core.periods import MONTH, YEAR @@ -22,11 +21,12 @@ class income_tax(Variable): entity = Person definition_period = MONTH label = "Income tax" - reference = "https://law.gov.example/income_tax" # Always use the most official source + reference = ( + "https://law.gov.example/income_tax" # Always use the most official source + ) def formula(person, period, parameters): - """ - Income tax. + """Income tax. The formula to compute the income tax for a given person at a given period """ @@ -38,11 +38,11 @@ class social_security_contribution(Variable): entity = Person definition_period = MONTH label = "Progressive contribution paid on salaries to finance social security" - reference = "https://law.gov.example/social_security_contribution" # Always use the most official source + # Always use the most official source + reference = "https://law.gov.example/social_security_contribution" def formula(person, period, parameters): - """ - Social security contribution. + """Social security contribution. The social_security_contribution is computed according to a marginal scale. """ @@ -57,25 +57,32 @@ class housing_tax(Variable): entity = Household definition_period = YEAR # This housing tax is defined for a year. label = "Tax paid by each household proportionally to the size of its accommodation" - reference = "https://law.gov.example/housing_tax" # Always use the most official source + reference = ( + "https://law.gov.example/housing_tax" # Always use the most official source + ) def formula(household, period, parameters): - """ - Housing tax. + """Housing tax. - The housing tax is defined for a year, but depends on the `accommodation_size` and `housing_occupancy_status` on the first month of the year. - Here period is a year. We can get the first month of a year with the following shortcut. - To build different periods, see https://openfisca.org/doc/coding-the-legislation/35_periods.html#calculate-dependencies-for-a-specific-period + The housing tax is defined for a year, but depends on the `accommodation_size` + and `housing_occupancy_status` on the first month of the year. Here period + is a year. We can get the first month of a year with the following shortcut. + To build different periods, see + https://openfisca.org/doc/coding-the-legislation/35_periods.html#calculate-dependencies-for-a-specific-period """ january = period.first_month accommodation_size = household("accommodation_size", january) tax_params = parameters(period).taxes.housing_tax - tax_amount = max_(accommodation_size * tax_params.rate, tax_params.minimal_amount) + tax_amount = max_( + accommodation_size * tax_params.rate, tax_params.minimal_amount + ) # `housing_occupancy_status` is an Enum variable occupancy_status = household("housing_occupancy_status", january) - HousingOccupancyStatus = occupancy_status.possible_values # Get the enum associated with the variable + HousingOccupancyStatus = ( + occupancy_status.possible_values + ) # Get the enum associated with the variable # To access an enum element, we use the `.` notation. tenant = occupancy_status == HousingOccupancyStatus.tenant owner = occupancy_status == HousingOccupancyStatus.owner diff --git a/pyproject.toml b/pyproject.toml index 6c97619c..88fe379c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "openfisca-country_template" version = "7.1.5" dependencies = [ - "openfisca-core[web-api] >= 42.0.0, <43.0.0", + "openfisca-core[web-api] >= 43", ] requires-python = ">=3.9" authors = [] @@ -23,11 +23,10 @@ classifiers = [ [project.optional-dependencies] dev = [ - "autopep8 >=2.0.4", - "flake8 >=7.0.0", - "isort >=5.13.2", - "pylint >=3.1.0", - "pyupgrade >=3.15.1", + "black >=24.8.0, <25.0", + "isort >=5.13.2, <6.0", + "ruff >=0.6.9, <1.0", + "ruff-lsp >=0.0.57, <1.0", "yamllint >=1.35.1" ] @@ -39,57 +38,65 @@ Issues = "https://github.com/openfisca/country-template/issues" Changelog = "https://github.com/openfisca/country-template/blob/main/CHANGELOG.md" -[tool.pytest.ini_options] -addopts = "--showlocals --doctest-modules" -testpaths = [ "openfisca_country_template/tests" ] -python_files = "**/*.py" -filterwarnings = [ - "error", - "ignore::UserWarning", - 'ignore:function ham\(\) is deprecated:DeprecationWarning' -] - -[tool.pylint.messages_control] -disable = [ - "invalid-name", - "missing-class-docstring", - "missing-function-docstring", - "line-too-long", - "no-self-argument", - "invalid-enum-extension", - "no-member", - "not-callable", - "duplicate-code", - "too-many-locals", - "fixme", - "unused-argument", - "redefined-outer-name" -] -score = "no" +[tool.black] +target_version = ["py39", "py310", "py311"] [tool.isort] -case_sensitive = "true" -force_alphabetical_sort_within_sections = "false" -group_by_package = "true" -include_trailing_comma = "true" -multi_line_output = "8" -py_version = "39" -known_first_party = "openfisca_country_template" -known_openfisca = [ - "openfisca_core", - "openfisca_country_template" -] -known_typing = [ - "mypy*", - "*types*", - "*typing*" -] +case_sensitive = true +combine_as_imports = true +force_alphabetical_sort_within_sections = false +group_by_package = true +honor_noqa = true +include_trailing_comma = true +known_first_party = ["openfisca_country_template"] +known_openfisca = ["openfisca_core", "openfisca_extension_template"] +known_typing = ["*collections.abc*", "*typing*", "*typing_extensions*"] +known_types = ["*types*"] +multi_line_output = 3 +profile = "black" +py_version = 39 sections = [ "FUTURE", "TYPING", + "TYPES", "STDLIB", "THIRDPARTY", "OPENFISCA", "FIRSTPARTY", - "LOCALFOLDER" + "LOCALFOLDER", ] + +[tool.pytest.ini_options] +addopts = "--exitfirst --showlocals --doctest-modules" +testpaths = [ "openfisca_country_template/tests" ] +python_files = "**/*.py" +filterwarnings = ["ignore::DeprecationWarning"] + +[tool.ruff] +target-version = "py39" + +[tool.ruff.format] +docstring-code-format = true +docstring-code-line-length = 72 + +[tool.ruff.lint] +ignore = [ + "ANN", + "COM812", + "D101", + "D104", + "I001", + "ISC001", + "N801", + "N805", + "N806", + "PLR2004", + "PTH100", + "PTH118", + "PTH120", + "PTH123", +] +select = ["ALL"] + +[tool.ruff.lint.pydocstyle] +convention = "google" From ea59d9cbeeb658f20eb227d39e4747abb6db1754 Mon Sep 17 00:00:00 2001 From: Mauko Quiroga Date: Mon, 14 Oct 2024 16:24:56 +0200 Subject: [PATCH 2/4] chore: version bump --- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e59ceff..af454e30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +### 7.1.6 [#156](https://github.com/openfisca/country-template/pull/156) + +* Minor change. +* Impacted areas: `**/*` +* Details: + - Normalize `pyproject.toml` + - Update OpenFisca-Core to 43.0.0 + ### 7.1.5 [#154](https://github.com/openfisca/country-template/pull/154) * Minor change. diff --git a/pyproject.toml b/pyproject.toml index 88fe379c..7aade143 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openfisca-country_template" -version = "7.1.5" +version = "7.1.6" dependencies = [ "openfisca-core[web-api] >= 43", ] From 69d065ef29c0ae6d7f9f681af6490386e20f51b2 Mon Sep 17 00:00:00 2001 From: Mauko Quiroga Date: Tue, 15 Oct 2024 13:21:39 +0200 Subject: [PATCH 3/4] ci: remove duplicate lint check --- .github/lint-files.sh | 32 -------------------------------- .github/workflows/validate.yml | 4 +--- 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100755 .github/lint-files.sh diff --git a/.github/lint-files.sh b/.github/lint-files.sh deleted file mode 100755 index 507dff81..00000000 --- a/.github/lint-files.sh +++ /dev/null @@ -1,32 +0,0 @@ -#! /usr/bin/env bash - -# Usage: lint-files.sh -# -# Example usage: -# lint-files.sh "*.py" "flake8" -# lint-files.sh "tests/*.yaml" "yamllint" - -file_pattern=$1 -linter_command=$2 - -if [ -z "$file_pattern" ] || [ -z "$linter_command" ] -then - echo "Usage: $0 " - exit 1 -fi - -last_tagged_commit=$(git describe --tags --abbrev=0 --first-parent 2>/dev/null) # Attempt to find the last tagged commit in the direct ancestry of the main branch avoiding tags introduced by merge commits from other branches - -if [ -z "$last_tagged_commit" ] -then - last_tagged_commit=$(git rev-list --max-parents=0 HEAD) # Fallback to finding the root commit if no tags are present -fi - -if ! changed_files=$(git diff-index --name-only --diff-filter=ACMR --exit-code $last_tagged_commit -- "$file_pattern") -then - echo "Linting the following files:" - echo "$changed_files" - $linter_command $changed_files -else - echo "No changed files matching pattern '$file_pattern' to lint." -fi diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 1072fbcc..33d9b941 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -30,10 +30,8 @@ jobs: - run: make check-syntax-errors - - run: make check-style - - name: Lint Python files - run: "${GITHUB_WORKSPACE}/.github/lint-files.sh '*.py' 'flake8'" + run: make check-style - name: Lint YAML tests run: "${GITHUB_WORKSPACE}/.github/lint-files.sh 'tests/*.yaml' 'yamllint'" From 17f94ad0e1315d513b386874fadba8981de39d85 Mon Sep 17 00:00:00 2001 From: Mauko Quiroga Date: Tue, 15 Oct 2024 14:20:06 +0200 Subject: [PATCH 4/4] ci: fix yamllint --- .github/workflows/validate.yml | 2 +- Makefile | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 33d9b941..56087b87 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -34,7 +34,7 @@ jobs: run: make check-style - name: Lint YAML tests - run: "${GITHUB_WORKSPACE}/.github/lint-files.sh 'tests/*.yaml' 'yamllint'" + run: make check-yaml test-yaml: runs-on: ubuntu-22.04 diff --git a/Makefile b/Makefile index 978269a2..7414e72d 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,11 @@ check-style: isort --check `git ls-files | grep "\.py$$"` black --check `git ls-files | grep "\.py$$"` +check-yaml: + @# Do not analyse .gitignored files. + @# `make` needs `$$` to output `$`. Ref: http://stackoverflow.com/questions/2382764. + yamllint `git ls-files | grep "\.yaml$$"` + test: clean check-syntax-errors check-style openfisca test --country-package openfisca_country_template openfisca_country_template/tests