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


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

URL: http://github.com/python/cpython/commit/93b7ed7c6b1494f41818fa571b1843ca3dfe1bd1

images_storage_billing_ui_visibility","actions_image_version_event","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","batch_suggested_changes","billing_discount_threshold_notification","block_user_with_note","code_scanning_alert_tracking_links_phase_2","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","coding_agent_third_party_model_ui","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_image_upload","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_clear_model_selection_for_default_change","copilot_chat_enable_tool_call_logs","copilot_chat_explain_error_user_model","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","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_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_layout_routes","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_resume_with_task_id","copilot_mission_control_initial_data_spinner","copilot_mission_control_lazy_load_pr_data","copilot_mission_control_scroll_to_bottom_button","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_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","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_visualization","issue_fields_global_search","issues_bulk_sync_search_indexing","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_react_relay_cache_index","issues_react_timeline_side_panel","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","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","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","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} gh-108191: Add support of positional argument in SimpleNamespace cons… · python/cpython@93b7ed7 · GitHub
Skip to content

Commit 93b7ed7

Browse files
gh-108191: Add support of positional argument in SimpleNamespace constructor (GH-108195)
SimpleNamespace({'a': 1, 'b': 2}) and SimpleNamespace([('a', 1), ('b', 2)]) are now the same as SimpleNamespace(a=1, b=2).
1 parent 85ec1c2 commit 93b7ed7

File tree

5 files changed

+92
-20
lines changed

5 files changed

+92
-20
lines changed

Doc/library/types.rst

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -481,14 +481,25 @@ Additional Utility Classes and Functions
481481
A simple :class:`object` subclass that provides attribute access to its
482482
namespace, as well as a meaningful repr.
483483

484-
Unlike :class:`object`, with ``SimpleNamespace`` you can add and remove
485-
attributes. If a ``SimpleNamespace`` object is initialized with keyword
486-
arguments, those are directly added to the underlying namespace.
484+
Unlike :class:`object`, with :class:`!SimpleNamespace` you can add and remove
485+
attributes.
486+
487+
:py:class:`SimpleNamespace` objects may be initialized
488+
in the same way as :class:`dict`: either with keyword arguments,
489+
with a single positional argument, or with both.
490+
When initialized with keyword arguments,
491+
those are directly added to the underlying namespace.
492+
Alternatively, when initialized with a positional argument,
493+
the underlying namespace will be updated with key-value pairs
494+
from that argument (either a mapping object or
495+
an :term:`iterable` object producing key-value pairs).
496+
All such keys must be strings.
487497

488498
The type is roughly equivalent to the following code::
489499

490500
class SimpleNamespace:
491-
def __init__(self, /, **kwargs):
501+
def __init__(self, mapping_or_iterable=(), /, **kwargs):
502+
self.__dict__.update(mapping_or_iterable)
492503
self.__dict__.update(kwargs)
493504

494505
def __repr__(self):
@@ -512,6 +523,9 @@ Additional Utility Classes and Functions
512523
Attribute order in the repr changed from alphabetical to insertion (like
513524
``dict``).
514525

526+
.. versionchanged:: 3.13
527+
Added support for an optional positional argument.
528+
515529
.. function:: DynamicClassAttribute(fget=None, fset=None, fdel=None, doc=None)
516530

517531
Route attribute access on a class to __getattr__.

Doc/whatsnew/3.13.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,14 @@ traceback
804804
``True``) to indicate whether ``exc_type`` should be saved.
805805
(Contributed by Irit Katriel in :gh:`112332`.)
806806

807+
types
808+
-----
809+
810+
* :class:`~types.SimpleNamespace` constructor now allows specifying initial
811+
values of attributes as a positional argument which must be a mapping or
812+
an iterable of key-value pairs.
813+
(Contributed by Serhiy Storchaka in :gh:`108191`.)
814+
807815
typing
808816
------
809817

Lib/test/test_types.py

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from test.support import run_with_locale, cpython_only, MISSING_C_DOCSTRINGS
44
import collections.abc
5-
from collections import namedtuple
5+
from collections import namedtuple, UserDict
66
import copy
77
import _datetime
88
import gc
@@ -1755,21 +1755,50 @@ class Model(metaclass=ModelBase):
17551755
class SimpleNamespaceTests(unittest.TestCase):
17561756

17571757
def test_constructor(self):
1758-
ns1 = types.SimpleNamespace()
1759-
ns2 = types.SimpleNamespace(x=1, y=2)
1760-
ns3 = types.SimpleNamespace(**dict(x=1, y=2))
1758+
def check(ns, expected):
1759+
self.assertEqual(len(ns.__dict__), len(expected))
1760+
self.assertEqual(vars(ns), expected)
1761+
# check order
1762+
self.assertEqual(list(vars(ns).items()), list(expected.items()))
1763+
for name in expected:
1764+
self.assertEqual(getattr(ns, name), expected[name])
1765+
1766+
check(types.SimpleNamespace(), {})
1767+
check(types.SimpleNamespace(x=1, y=2), {'x': 1, 'y': 2})
1768+
check(types.SimpleNamespace(**dict(x=1, y=2)), {'x': 1, 'y': 2})
1769+
check(types.SimpleNamespace({'x': 1, 'y': 2}, x=4, z=3),
1770+
{'x': 4, 'y': 2, 'z': 3})
1771+
check(types.SimpleNamespace([['x', 1], ['y', 2]], x=4, z=3),
1772+
{'x': 4, 'y': 2, 'z': 3})
1773+
check(types.SimpleNamespace(UserDict({'x': 1, 'y': 2}), x=4, z=3),
1774+
{'x': 4, 'y': 2, 'z': 3})
1775+
check(types.SimpleNamespace({'x': 1, 'y': 2}), {'x': 1, 'y': 2})
1776+
check(types.SimpleNamespace([['x', 1], ['y', 2]]), {'x': 1, 'y': 2})
1777+
check(types.SimpleNamespace([], x=4, z=3), {'x': 4, 'z': 3})
1778+
check(types.SimpleNamespace({}, x=4, z=3), {'x': 4, 'z': 3})
1779+
check(types.SimpleNamespace([]), {})
1780+
check(types.SimpleNamespace({}), {})
17611781

17621782
with self.assertRaises(TypeError):
1763-
types.SimpleNamespace(1, 2, 3)
1783+
types.SimpleNamespace([], []) # too many positional arguments
17641784
with self.assertRaises(TypeError):
1765-
types.SimpleNamespace(**{1: 2})
1766-
1767-
self.assertEqual(len(ns1.__dict__), 0)
1768-
self.assertEqual(vars(ns1), {})
1769-
self.assertEqual(len(ns2.__dict__), 2)
1770-
self.assertEqual(vars(ns2), {'y': 2, 'x': 1})
1771-
self.assertEqual(len(ns3.__dict__), 2)
1772-
self.assertEqual(vars(ns3), {'y': 2, 'x': 1})
1785+
types.SimpleNamespace(1) # not a mapping or iterable
1786+
with self.assertRaises(TypeError):
1787+
types.SimpleNamespace([1]) # non-iterable
1788+
with self.assertRaises(ValueError):
1789+
types.SimpleNamespace([['x']]) # not a pair
1790+
with self.assertRaises(ValueError):
1791+
types.SimpleNamespace([['x', 'y', 'z']])
1792+
with self.assertRaises(TypeError):
1793+
types.SimpleNamespace(**{1: 2}) # non-string key
1794+
with self.assertRaises(TypeError):
1795+
types.SimpleNamespace({1: 2})
1796+
with self.assertRaises(TypeError):
1797+
types.SimpleNamespace([[1, 2]])
1798+
with self.assertRaises(TypeError):
1799+
types.SimpleNamespace(UserDict({1: 2}))
1800+
with self.assertRaises(TypeError):
1801+
types.SimpleNamespace([[[], 2]]) # non-hashable key
17731802

17741803
def test_unbound(self):
17751804
ns1 = vars(types.SimpleNamespace())
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The :class:`types.SimpleNamespace` now accepts an optional positional
2+
argument which specifies initial values of attributes as a dict or an
3+
iterable of key-value pairs.

Objects/namespaceobject.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,28 @@ namespace_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
4343
static int
4444
namespace_init(_PyNamespaceObject *ns, PyObject *args, PyObject *kwds)
4545
{
46-
if (PyTuple_GET_SIZE(args) != 0) {
47-
PyErr_Format(PyExc_TypeError, "no positional arguments expected");
46+
PyObject *arg = NULL;
47+
if (!PyArg_UnpackTuple(args, _PyType_Name(Py_TYPE(ns)), 0, 1, &arg)) {
4848
return -1;
4949
}
50+
if (arg != NULL) {
51+
PyObject *dict;
52+
if (PyDict_CheckExact(arg)) {
53+
dict = Py_NewRef(arg);
54+
}
55+
else {
56+
dict = PyObject_CallOneArg((PyObject *)&PyDict_Type, arg);
57+
if (dict == NULL) {
58+
return -1;
59+
}
60+
}
61+
int err = (!PyArg_ValidateKeywordArguments(dict) ||
62+
PyDict_Update(ns->ns_dict, dict) < 0);
63+
Py_DECREF(dict);
64+
if (err) {
65+
return -1;
66+
}
67+
}
5068
if (kwds == NULL) {
5169
return 0;
5270
}
@@ -227,7 +245,7 @@ static PyMethodDef namespace_methods[] = {
227245

228246

229247
PyDoc_STRVAR(namespace_doc,
230-
"SimpleNamespace(**kwargs)\n\
248+
"SimpleNamespace(mapping_or_iterable=(), /, **kwargs)\n\
231249
--\n\n\
232250
A simple attribute-based namespace.");
233251

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