Unclear usage of `member = ISubscriptionManager(mlist).register(…)`
Hello,
I am trying to understand the source code. My reading is, that interfaces/subscriptions.py:class ISubscriptionManager.register always returns a 3-tuple and the last element of the tuple is a member object, or None. This coincides with the implementation in app/subscriptions.py.SubscriptionManager.register . cli_synmembers.py:add_members() contains:
member = registrar.register(
subscriber,
pre_verified=True,
pre_approved=True,
pre_confirmed=True,
send_welcome_message=welcome_msg)[2]
member.preferences.delivery_status = delivery_status
member.preferences.delivery_mode = delivery_mode
return
and commands/cli_addmembers.py:add_members() contains
member = registrar.register(
subscriber,
pre_verified=True,
pre_approved=True,
pre_confirmed=True,
invitation=invite,
send_welcome_message=welcome_msg)[2]
if member is not None:
member.preferences.delivery_status = delivery_status
member.preferences.delivery_mode = delivery_mode
return
So my question is: when regirstar.register returns a tuple, the last
argument of which is a members, why do the above snippets store the
result in a variable called member
?
How are the set preferences set to member handled further, as my reading is that the variable just disappears, immediately after member.preferences is set?
Greeting Дилян
On 2/25/22 12:16, Дилян Палаузов wrote:
Hello,
I am trying to understand the source code. My reading is, that interfaces/subscriptions.py:class ISubscriptionManager.register always returns a 3-tuple and the last element of the tuple is a member object, or None. This coincides with the implementation in app/subscriptions.py.SubscriptionManager.register . cli_synmembers.py:add_members() contains:
member = registrar.register( subscriber, pre_verified=True, pre_approved=True, pre_confirmed=True, send_welcome_message=welcome_msg)[2] member.preferences.delivery_status = delivery_status member.preferences.delivery_mode = delivery_mode return
and commands/cli_addmembers.py:add_members() contains
member = registrar.register( subscriber, pre_verified=True, pre_approved=True, pre_confirmed=True, invitation=invite, send_welcome_message=welcome_msg)[2] if member is not None: member.preferences.delivery_status = delivery_status member.preferences.delivery_mode = delivery_mode return
So my question is: when regirstar.register returns a tuple, the last argument of which is a members, why do the above snippets store the result in a variable called
member
?
The above snippets say member = registrar.register(...)[2]
I.e. member is set to the last element of the 3-tuple.
-- Mark Sapiro <mark@msapiro.net> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan
I hope I'm not underestimating your understanding. If so, no offense intended. I don't think Mark's answer entirely addressed your question, so here goes.
Дилян Палаузов writes:
I am trying to understand the source code. My reading is, that interfaces/subscriptions.py:class ISubscriptionManager.register always returns a 3-tuple and the last element of the tuple is a member object, or None. This coincides with the implementation in app/subscriptions.py.SubscriptionManager.register . cli_synmembers.py:add_members() contains:
member = [...]
[...] and commands/cli_addmembers.py:add_members() contains
member = [...]
So my question is: when regirstar.register returns a tuple, the last argument of which is a members,
It's not a members (plural). The last element is a member (singular), as the code accessing it indicates.
why do the above snippets store the result in a variable called
member
?
If you look a little bit earlier in cli_addmembers.py:add_members (this one is plural!), you see
for line in in_fp:
# line parsing stuff and then
try:
member = [...] # the line quoted from your message above
What is in_fp? It's a file object returned from open(). The structure of that file is a sequence of lines, where each line contains either a display name and an address enclosed in <>, or an address, to be tested for membership, and if not a member to be registered (both of those are encapsulated in registrar.register, the result of the test is communicated by raising AlreadySubscribedError or not).
So cli_addmembers is able to handle more than one subscription at a time, but it passes them to register one at a time. That's why the function's name is plural, but the local variable name is singular.
How are the set preferences set to member handled further, as my reading is that the variable just disappears, immediately after member.preferences is set?
The one-line answer is "it's SQLAlchemy 'magic'." :-)
Note the decorator "@transactional* at the top. That says that this function manipulates the external database in sqlite3, postgresql, or mysql, and if it fails in any way, all of it should be rolled back and not entered into the database. How this connection to a RDBMS works is too complicated to explain it detail here, but the basic idea is that the member object returned from registrar.register is endowed with properties that call SQLAlchemy to add or update the member records in the external database. This makes it possible for you, the programmer, to manipulate objects like member as ordinary Python objects, but changes you make to them are reflected persistently in a database.
OK, a little more detail since you'd need to study SQLAlchemy: basically what an object-relational manager like SQLAlchemy does is to provide code that accesses and caches data from the RDMBS when an object's attributes are accessed, and more code that issues database updates when they are changed. @transactional wraps the function it decorates in code that first creates an RDMBS transaction, then calls the function. This means that the database code does not update the external database immediately, but queues the updates in the transaction. Finally, if the function exits normally, @transactional regains control and executes the transaction, making all updates to the database.
All this means that classes defined in a model/*.py have instances that can be treated as persisting even after the program exits. This is actually quite natural if you consider how Python variables are not variables in the sense of a data container in memory, but just name- object bindings.
participants (3)
-
Mark Sapiro
-
Stephen J. Turnbull
-
Дилян Палаузов