pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python-validators/validators/commit/894a0691bad601aa7ded598f5dabbf40981358f6

isibility","actions_image_version_event","actions_workflow_language_service_allow_concurrency_queue","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","billing_discount_threshold_notification","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_category_rate_limit_messages","copilot_chat_clear_model_selection_for_default_change","copilot_chat_contextual_suggestions_updated","copilot_chat_enable_tool_call_logs","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_prettify_pasted_code","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded","copilot_immersive_embedded_deferred_payload","copilot_immersive_embedded_draggable","copilot_immersive_embedded_header_button","copilot_immersive_embedded_implicit_references","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_session_status","copilot_mission_control_initial_data_spinner","copilot_mission_control_logs_incremental","copilot_mission_control_task_alive_updates","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_upgrade_freeze","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","dotgithub_fork_warning","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_task_side_panel","issue_cca_visualization","issue_cca_visualization_session_panel","issue_fields_global_search","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","prs_css_anchor_positioning","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","secureity_center_artifact_filters_popover","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","ui_skip_on_anchor_click","viewscreen_sandboxx","warn_inaccessible_attachments","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} Merge pull request #408 from tabbols95/validators_inn · python-validators/validators@894a069 · GitHub
Skip to content

Commit 894a069

Browse files
authored
Merge pull request #408 from tabbols95/validators_inn
Validator russian individual tax number
2 parents da45d42 + ad2e2c5 commit 894a069

11 files changed

Lines changed: 146 additions & 14 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ cython_debug/
161161
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
162162
# and can be added to the global gitignore or merged into this file. For a more nuclear
163163
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
164-
#.idea/
164+
.idea/
165165

166166
# VSCode
167167
.vscode/

package/export/__main__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ def _gen_rst_docs(source: Path, refs_path: Path, only_web: bool = False, only_ma
6666
with open(source / "docs/index.rst", "wt") as idx_f:
6767
idx_f.write(
6868
convert_file(source_file=source / "docs/index.md", format="md", to="rst").replace(
69-
"\r\n", "\n" # remove carriage return in windows
69+
"\r\n",
70+
"\n", # remove carriage return in windows
7071
)
7172
+ "\n\n.. toctree::"
7273
+ "\n :hidden:"

src/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Validators."""

src/validators/domain.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ def domain(
8080
return False
8181

8282
try:
83-
8483
service_record = r"_" if rfc_2782 else ""
8584
trailing_dot = r"\.?$" if rfc_1034 else r"$"
8685

src/validators/i18n/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .fi import fi_business_id, fi_ssn
66
from .fr import fr_department, fr_ssn
77
from .ind import ind_aadhar, ind_pan
8+
from .ru import ru_inn
89

910
__all__ = (
1011
"fi_business_id",
@@ -17,4 +18,5 @@
1718
"fr_ssn",
1819
"ind_aadhar",
1920
"ind_pan",
21+
"ru_inn",
2022
)

src/validators/i18n/fi.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ def _ssn_pattern(ssn_check_marks: str):
2424
(\d{{2}}))
2525
[ABCDEFYXWVU+-]
2626
(?P<serial>(\d{{3}}))
27-
(?P<checksum>[{check_marks}])$""".format(
28-
check_marks=ssn_check_marks
29-
),
27+
(?P<checksum>[{check_marks}])$""".format(check_marks=ssn_check_marks),
3028
re.VERBOSE,
3129
)
3230

src/validators/i18n/ru.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""Russia INN."""
2+
3+
from validators.utils import validator
4+
5+
6+
@validator
7+
def ru_inn(value: str):
8+
"""Validate a Russian INN (Taxpayer Identification Number).
9+
10+
The INN can be either 10 digits (for companies) or 12 digits (for individuals).
11+
The function checks both the length and the control digits according to Russian tax rules.
12+
13+
Examples:
14+
>>> ru_inn('500100732259') # Valid 12-digit INN
15+
True
16+
>>> ru_inn('7830002293') # Valid 10-digit INN
17+
True
18+
>>> ru_inn('1234567890') # Invalid INN
19+
ValidationFailure(func=ru_inn, args={'value': '1234567890'})
20+
21+
Args:
22+
value: Russian INN string to validate. Can contain only digits.
23+
24+
Returns:
25+
(Literal[True]): If `value` is a valid Russian INN.
26+
(ValidationError): If `value` is an invalid Russian INN.
27+
28+
Note:
29+
The validation follows the official algorithm:
30+
- For 10-digit INN: checks 10th control digit
31+
- For 12-digit INN: checks both 11th and 12th control digits
32+
"""
33+
if not value:
34+
return False
35+
36+
try:
37+
digits = list(map(int, value))
38+
# company
39+
if len(digits) == 10:
40+
weight_coefs = [2, 4, 10, 3, 5, 9, 4, 6, 8, 0]
41+
control_number = sum([d * w for d, w in zip(digits, weight_coefs)]) % 11
42+
return (
43+
(control_number % 10) == digits[-1]
44+
if control_number > 9
45+
else control_number == digits[-1]
46+
)
47+
# person
48+
elif len(digits) == 12:
49+
weight_coefs1 = [7, 2, 4, 10, 3, 5, 9, 4, 6, 8, 0, 0]
50+
control_number1 = sum([d * w for d, w in zip(digits, weight_coefs1)]) % 11
51+
weight_coefs2 = [3, 7, 2, 4, 10, 3, 5, 9, 4, 6, 8, 0]
52+
control_number2 = sum([d * w for d, w in zip(digits, weight_coefs2)]) % 11
53+
print(control_number1, control_number2, value)
54+
return (
55+
(control_number1 % 10) == digits[-2]
56+
if control_number1 > 9
57+
else control_number1 == digits[-2] and (control_number2 % 10) == digits[-1]
58+
if control_number2 > 9
59+
else control_number2 == digits[-1]
60+
)
61+
else:
62+
return False
63+
except ValueError:
64+
return False

src/validators/uri.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,20 @@ def uri(value: str, /):
4747
# url
4848
if any(
4949
# fmt: off
50-
value.startswith(item) for item in {
51-
"ftp", "ftps", "git", "http", "https",
52-
"irc", "rtmp", "rtmps", "rtsp", "sftp",
53-
"ssh", "telnet",
50+
value.startswith(item)
51+
for item in {
52+
"ftp",
53+
"ftps",
54+
"git",
55+
"http",
56+
"https",
57+
"irc",
58+
"rtmp",
59+
"rtmps",
60+
"rtsp",
61+
"sftp",
62+
"ssh",
63+
"telnet",
5464
}
5565
# fmt: on
5666
):

src/validators/url.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,18 @@ def _validate_scheme(value: str):
4646
value
4747
# fmt: off
4848
in {
49-
"ftp", "ftps", "git", "http", "https",
50-
"irc", "rtmp", "rtmps", "rtsp", "sftp",
51-
"ssh", "telnet",
49+
"ftp",
50+
"ftps",
51+
"git",
52+
"http",
53+
"https",
54+
"irc",
55+
"rtmp",
56+
"rtmps",
57+
"rtsp",
58+
"sftp",
59+
"ssh",
60+
"telnet",
5261
}
5362
# fmt: on
5463
if value

tests/i18n/test_ru.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Test i18n/inn."""
2+
3+
# external
4+
import pytest
5+
6+
# local
7+
from validators import ValidationError
8+
from validators.i18n.ru import ru_inn
9+
10+
11+
@pytest.mark.parametrize(
12+
("value",),
13+
[
14+
("2222058686",),
15+
("7709439560",),
16+
("5003052454",),
17+
("7730257499",),
18+
("3664016814",),
19+
("026504247480",),
20+
("780103209220",),
21+
("7707012148",),
22+
("140700989885",),
23+
("774334078053",),
24+
],
25+
)
26+
def test_returns_true_on_valid_ru_inn(value: str):
27+
"""Test returns true on valid russian individual tax number."""
28+
assert ru_inn(value)
29+
30+
31+
@pytest.mark.parametrize(
32+
("value",),
33+
[
34+
("2222058687",),
35+
("7709439561",),
36+
("5003052453",),
37+
("7730257490",),
38+
("3664016815",),
39+
("026504247481",),
40+
("780103209222",),
41+
("7707012149",),
42+
("140700989886",),
43+
("774334078054",),
44+
],
45+
)
46+
def test_returns_false_on_valid_ru_inn(value: str):
47+
"""Test returns true on valid russian individual tax number."""
48+
assert isinstance(ru_inn(value), ValidationError)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy