GithubHelp home page GithubHelp logo

transaction's Introduction

transaction's People

Contributors

agroszer avatar cguardia avatar cito avatar d-maurer avatar dataflake avatar di avatar freddrake avatar hugovk avatar icemac avatar jamadden avatar jelly avatar jimfulton avatar jugmac00 avatar kapyshin avatar lrowe avatar malthe avatar mcdonc avatar mgedmin avatar mmerickel avatar mpeeters avatar mwatts15 avatar nandoflorestan avatar philikon avatar ronnix avatar rouge8 avatar sjustas avatar srichter avatar strichter avatar tseaver avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

transaction's Issues

1.4.3 is in PyPI

Transaction 1.4.3 is currently the latest release in PyPI, but the changelog doesn't mention that it has been released, nor what changes it contains.

Perhaps it was released mistakenly?

RTD integration broken

Github services have been deprecated, and the existing service for RTD cannot be re-activated. Hence, docs aren't getting built. It needs migrated to a webhook. To do that, we need the project's ID, which is only available to the RTD admin. Could @tseaver please take a look?

3.0.1: sphinx warnings `reference target not found`

On building my packages I'm using sphinx-build command with -n switch which shows warmings about missing references. These are not critical issues.
Here is the output with warnings:

+ /usr/bin/sphinx-build -n -T -b man docs build/sphinx/man
Running Sphinx v5.0.1
making output directory... done
loading intersphinx inventory from https://docs.python.org/objects.inv...
loading intersphinx inventory from http://www.zodb.org/en/latest/objects.inv...
intersphinx inventory has moved: https://docs.python.org/objects.inv -> https://docs.python.org/3/objects.inv
intersphinx inventory has moved: http://www.zodb.org/en/latest/objects.inv -> https://zodb.org/en/latest/objects.inv
building [mo]: targets for 0 po files that are out of date
building [man]: all manpages
updating environment: [new config] 11 added, 0 changed, 0 removed
reading sources... [100%] sqlalchemy
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
writing... python-transaction.3 { changes convenience doom savepoint hooks datamanager integrations sqlalchemy api developer } /home/tkloczko/rpmbuild/BUILD/transaction-3.0.1/docs/hooks.rst:8: WARNING: unknown document: resourcemanager
/home/tkloczko/rpmbuild/BUILD/transaction-3.0.1/docs/hooks.rst:18: WARNING: py:meth reference target not found: addBeforeCommitHook
/home/tkloczko/rpmbuild/BUILD/transaction-3.0.1/docs/hooks.rst:184: WARNING: py:meth reference target not found: addAfterCommitHook
/home/tkloczko/rpmbuild/BUILD/transaction-3.0.1/src/transaction/interfaces.py:docstring of transaction.interfaces.ITransactionManager:5: WARNING: py:obj reference target not found: __enter__
/home/tkloczko/rpmbuild/BUILD/transaction-3.0.1/src/transaction/interfaces.py:docstring of transaction.interfaces.ITransactionManager:5: WARNING: py:obj reference target not found: __exit__
docstring of transaction.interfaces.ITransactionManager.registeredSynchs:1: WARNING: py:obj reference target not found: ISynchronizers
docstring of transaction.interfaces.ITransaction.setExtendedInfo:: WARNING: py:class reference target not found: text
docstring of transaction.interfaces.ITransaction.setExtendedInfo:15: WARNING: py:obj reference target not found: <name, value>
docstring of transaction.interfaces.ITransaction.addBeforeCommitHook:: WARNING: py:class reference target not found: sequence
docstring of transaction.interfaces.IDataManager.sortKey:1: WARNING: py:obj reference target not found: IDataManagers
/home/tkloczko/rpmbuild/BUILD/transaction-3.0.1/src/transaction/_manager.py:docstring of transaction._manager.ThreadTransactionManager:1: WARNING: py:class reference target not found: _thread._local
done
build succeeded, 11 warnings.

You can peak on fixes that kind of issues in other projects
latchset/jwcrypto#289
click-contrib/sphinx-click@abc31069
latchset/jwcrypto#289
RDFLib/rdflib-sqlalchemy#95
sissaschool/elementpath@bf869d9e
jaraco/cssutils#21

an object can join a transaction multiple times

While working on the mail package, I noticed that a resource could join a transaction multiple times.

I'm not sure this if this is a bug or intended behavior. I couldn't think of any situation where a resource should join a transaction multiple times, so I wanted to report it. If this happens, seemingly random errors appear in the two-phase commit, as the object will have tpc_begin called for each join.

if there's no reason for multiple joins, it might make sense to raise an exception if something is joined a multiple time -- though this would also require tracking of the raw and wrapped resources ( line 162 ).

https://github.com/zopefoundation/transaction/blob/master/transaction/_transaction.py#L145-L179

explicit transactions do not work well with the context manager

I submitted this to the python-transaction mailing list but it didn't get picked up by anyone so I'm posting it here.

tm = transaction.TransactionManager(explicit=True)
with tm:
    tm.commit()

The tm.__exit__ raises a NoTransaction exception.

I think this use case should be considered valid and should not raise a NoTransaction exception. Specifically the __exit__ should be implemented to squash a NoTransaction exception or properly check if there is a transaction before calling commit() or abort().

add a public api for manager._retryable

In a web context we want to wrap as much of the request as possible, but we also want to render errors. This causes a circular dependency between the transaction manager and the system that renders errors.

In pyramid_tm the goal is to expose to the underlying code whether it is processing a retryable error and allow it to handle such errors differently if it wants. If we choose to handle the retryable error then we are effectively squashing the exception and thus the transaction.run() will see it as valid. I'd like to allow pyramid_tm to detect the retryable errors itself and raise exceptions if necessary. This depends on manager._retryable right now, but I'd be happier about it if the API itself were public.

A public api would make sense as well as it's intended to be an abstraction around retryable errors... without it each error handler in an app needs to check for arbitrary exceptions on a case-by-case basis instead of just checking that they are retryable.

Would manager.is_retryable(error) work? The error_type seems redundant to me. I'm happy to make the PR myself.

Jenkins failures on master

From: http://jenkins.pylonsproject.org/job/transaction/58/console

Coverage gap:

coverage inst-nodeps: /home/agendaless/jenkins/pylonsproject/jobs/transaction/workspace/.tox/dist/transaction-1.7.0.zip
coverage installed: coverage==4.2,funcsigs==1.0.2,mock==2.0.0,nose==1.3.7,nosexcover==1.0.10,pbr==1.10.0,six==1.10.0,transaction==1.7.0,zope.interface==4.3.2
coverage runtests: PYTHONHASHSEED='1290084312'
coverage runtests: commands[0] | nosetests --with-xunit --with-xcoverage
...................................................................................................................................................................................
Name                          Stmts   Miss Branch BrPart  Cover   Missing
-------------------------------------------------------------------------
transaction.py                   14      0      0      0   100%
transaction/_compat.py           14      0      0      0   100%
transaction/_manager.py         124      0     56      1    99%   181->exit
transaction/_transaction.py     393      0    116      0   100%
transaction/interfaces.py         7      0      0      0   100%
transaction/weakset.py           22      0      4      0   100%
-------------------------------------------------------------------------
TOTAL                           574      0    176      1    99%
nose.plugins.cover: ERROR: TOTAL Coverage did not reach minimum required: 100%

Doctest repr:

Document: convenience
---------------------
**********************************************************************
File "convenience.rst", line 30, in default
Failed example:
    dm.last_note
Expected:
    'test 3'
Got:
    u'test 3'
**********************************************************************
1 items had failures:
   1 of   8 in default
8 tests in 1 items.
7 passed and 1 failed.
***Test Failed*** 1 failures.

Print the stack when a resource manager joins a transaction

For read-only ZODB connections, I sometimes have the problem that I've accidentally modified the database, causing an error when the transaction commits. Tracking down the offending operation is easy enough, but it requires modifying either transaction or ZODB, which is somewhat inconvenient. It would be nice to either have a logger (i.e., as defined in the logging module) that prints the stack when a transaction is joined. The logger could be disabled by default.

Attemps are retried on successful run

I'm not sure if this is a bug or intended behavior. But the attempts function does work as described in the example code in the documents:

for i in range(3):
    try:
       with transaction.manager:
           ... some something ...
    except SomeTransientException:
       contine
    else:
       break

Instead it works as:

for i in range(3):
    try:
       with transaction.manager:
           ... some something ...
    except SomeTransientException:
       contine

A successful attempt will just try again, as shown in the following:

import transaction.tests.savepointsample
import transaction

dm = transaction.tests.savepointsample.SampleSavepointDataManager()
with transaction.manager:
    dm['ntry'] = 0
ntry = 0

import transaction.interfaces
class Retry(transaction.interfaces.TransientError):
    pass

for attempt in transaction.manager.attempts(6):
    with attempt as t:
        t.note('test')
        print("%s %s" % (dm['ntry'], ntry))
        ntry += 1
        dm['ntry'] = ntry
        if ntry % 3:
            raise Retry(ntry)
print('current ntry: %s' % dm['ntry'])

The result:

0 0
0 1
0 2
3 3
3 4
3 5
current ntry 6

While if the documentation is to be believed I'd expect a result as follows:

0 0
0 1
0 2
current ntry 3

Thus my question is really what is correct, the documentation or the implementation?

Buffer instance error while commiting (python2.7)

While trying to use transaction.manager as context manager, I hit a buffer instance error with traceback (this is reproducible just by issuing a transaction.commit, too):

Here is a complete example (requires sqlalchemy -reproducible at least with 0.9.4 and 0.7.8), zope-sqlalchemy and transaction:

from sqlalchemy import MetaData, Column, Integer, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
import transaction
from zope.sqlalchemy import ZopeTransactionExtension
S = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
M = MetaData()
B = declarative_base(metadata=M)
engine = create_engine('sqlite:///:memory:', echo=True)
S.configure(bind=engine)
M.bind = engine
class Example(B):
    __tablename__ = "example"
    id = Column(Integer, primary_key=True)
    calc = Column(Integer)
M.create_all(engine)
with transaction.manager:
    S.add_all([Example(id=1, calc=10), Example(id=1, calc=11)])

As you can see, we are trying to insert the same id in a primary key column, something that has to raise an error and it does:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-de8e5b3d6d89> in <module>()
      1 with transaction.manager:
----> 2         S.add_all([Example(id=1, calc=10), Example(id=1, calc=11)])
      3 

/home/mariano/Code/SmD/env/local/lib/python2.7/site-packages/transaction-1.4.3-py2.7.egg/transaction/_manager.pyc in __exit__(self, t, v, tb)
    118     def __exit__(self, t, v, tb):
    119         if v is None:
--> 120             self.commit()
    121         else:
    122             self.abort()

/home/mariano/Code/SmD/env/local/lib/python2.7/site-packages/transaction-1.4.3-py2.7.egg/transaction/_manager.pyc in commit(self)
    109         """ See ITransactionManager.
    110         """
--> 111         return self.get().commit()
    112 
    113     def abort(self):

/home/mariano/Code/SmD/env/local/lib/python2.7/site-packages/transaction-1.4.3-py2.7.egg/transaction/_transaction.pyc in commit(self)
    276             tb = None
    277             try:
--> 278                 t, v, tb = self._saveAndGetCommitishError()
    279                 self._callAfterCommitHooks(status=False)
    280                 reraise(t, v, tb)

/home/mariano/Code/SmD/env/local/lib/python2.7/site-packages/transaction-1.4.3-py2.7.egg/transaction/_transaction.pyc in _saveAndGetCommitishError(self)
    298             t, v, tb = sys.exc_info()
    299             # Record how we got into commit().
--> 300             traceback.print_stack(sys._getframe(1), None, ft)
    301             # Append the stack entries from here down to the exception.
    302             traceback.print_tb(tb, None, ft)

/usr/lib/python2.7/traceback.pyc in print_stack(f, limit, file)
    267         except ZeroDivisionError:
    268             f = sys.exc_info()[2].tb_frame.f_back
--> 269     print_list(extract_stack(f, limit), file)
    270 
    271 def format_stack(f=None, limit=None):

/usr/lib/python2.7/traceback.pyc in print_list(extracted_list, file)
     23                '  File "%s", line %d, in %s' % (filename,lineno,name))
     24         if line:
---> 25             _print(file, '    %s' % line.strip())
     26 
     27 def format_list(extracted_list):

/usr/lib/python2.7/traceback.pyc in _print(file, str, terminator)
     11 
     12 def _print(file, str='', terminator='\n'):
---> 13     file.write(str+terminator)
     14 
     15 

TypeError: 'unicode' does not have the buffer interface

Remove legacy functionality from ITransactionDeprecated

There's a bunch of compatibility code supporting the old Transaction.register and DataManager.prepare APIs. That code is either documented as deprecated, commented as deprecated, or not documented at all. It hasn't been the supported protocol since transaction became its own distribution.

Removing it doesn't break any ZODB tests (except tests that are explicitly using testing the legacy functions).

No status to indicate aborted transaction.

This works:

>>> tx = tm.get()
>>> tx.status
'Active'
>>> tm.commit()
>>> tx.status
'Committed'

and this does’t:

>>> tx = tm.get()
>>> tx.status
'Active'
>>> tm.abort()
>>> tx.status
'Active'

Looking at the current status code definition:

class Status:
# ACTIVE is the initial state.
ACTIVE = "Active"
COMMITTING = "Committing"
COMMITTED = "Committed"
DOOMED = "Doomed"
# commit() or commit(True) raised an exception. All further attempts
# to commit or join this transaction will raise TransactionFailedError.
COMMITFAILED = "Commit failed"

then an "Aborted" status is missing, but I think would be quite sensible and useful to have.

And having said that, it would also make sense to either export the Status publicly or add is_active() (etc.) helper functions to the ITransaction interface so that the status of a transaction can be checked conveniently.

Fails to build on python 3.13: AttributeError: module 'unittest' has no attribute 'makeSuite'

BUG/PROBLEM REPORT / FEATURE REQUEST

What I did:

python-transaction fails to build on Fedora's python 3.13.
Fedora report:
https://bugzilla.redhat.com/show_bug.cgi?id=2245866

What actually happened:

python-transaction fails to build with Python 3.13.0a1.

__________________________________ test_suite __________________________________

    def test_suite():
>       return unittest.makeSuite(WeakSetTests)

src/transaction/tests/test_weakset.py:140: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'makeSuite'

    def __getattr__(name):
        if name == 'IsolatedAsyncioTestCase':
            global IsolatedAsyncioTestCase
            from .async_case import IsolatedAsyncioTestCase
            return IsolatedAsyncioTestCase
>       raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
E       AttributeError: module 'unittest' has no attribute 'makeSuite'

/usr/lib64/python3.13/unittest/__init__.py:80: AttributeError

=========================== short test summary info ============================
FAILED src/transaction/tests/test_weakset.py::test_suite - AttributeError: mo...
================== 1 failed, 176 passed, 2 warnings in 0.28s ===================

What version of Python and Zope/Addons I am using:

Fedora rawhide (Fedora 41), Python 3.13.0a1.

gevent.local.local breaks transaction.manager

This is not an issue with transaction per se, but an evil gotcha that might happen when one is using transaction with other libraries.

gevent is a coroutine based Python networking library. gevent offers functionality to make stdlib more co-routine friendly by monkey patching stdlib:

>>> import threading
>>> threading.local
<class '_thread._local'>

>>> from gevent import monkey
>>> gevent.monkey.patch_all()
>>> threading.local
<class 'gevent.local.local'>

Transaction has ThreadTransactionManager class that inherits from threading.local. If used in an environment where something has run gevent.monkey.patch_all() ThreadTransactionManager ceases to function properly and shares transactions across thread boundaries. This may be subtle breakage; one might not run issues until some race condition timing occurs.

I am currently figuring out if gevent can be made play nicely with transaction if they co-exist within the same process.

Still possible to join aborted and doomed transactions

Somewhat related to #64. Transaction.join functions the same whether a transaction is aborted or doomed or fully active. Should it allow that? What's the point of adding a resource manager to a transaction that can only be, or already has been, aborted?

On first glance this seems somewhat counterintuitive. I would have expected join to raise an exception like DoomedTransaction and AbortedTransaction instead of just allowing it. Maybe resource managers have already acquired resources before they make the call to join so it's important to be able to abort them later? But you can't currently abort transactions more than once, so that seems like an opportunity for bad behivour and leaks. join could automatically abort resource managers to cover that case.

def join(self, resource):
    if self.isAborted() or self.isDoomed():
        resource.abort()
        raise AbortedTransaction if self.isAborted() else DoomedTransaction
    # As before
    ...

The doom() documentation says the transaction is otherwise like normal, so for backwards compatibility this might be best limited to aborted transactions.

transaction 3.0 breaks afterCommitHooks

What I did:

A customer has an application using ZODB and zope.sqlalchemy running on transaction 2.4.0. I wanted to upgrade it to 3.0.
In the afterCommitHook the relational database is accessed via zope.sqlalchemy, changes are made and committed.
This worked in 2.4 because in tpc_finish the database session is closed and self._free() is called before calling the after commit hooks. This way the after commit hook had a clean state to start a new transaction.

What actually happened:

transaction 3.0 moved the self._free() call after calling the after commit hooks, see c76fd72#diff-b36b9bfe40a935a0303d1998fd1e0b8c1a062ced96eb4f92858e3b73409352c7L315-R323.

Using zope.sqlalcemy in the after commit hook now requires it to join the transaction because in tpc_finish it closed its session and forgot the knowledge about the zope transaction. But joining a transaction in the status committed is prohibited.

zope.sqlalchemy always seems to try to join the zope transaction. If that fails, it already had created the internal SQLAlchemy transaction. So using try-except in this case creates problems later on when SQLAlchemy thinks it does not have to join the zope transaction because it already has is internal transaction object initialized.

Blindly starting a new transaction in the application code used in the after commit hook is no solution as it is also used by code running inside the transaction.

Questions

  • Was the change of calling self._free() earlier intended to change a broken (?) behavior?
  • Are there any suggestions how to act inside an after commit hook now, when having the need to commit something? (Was this ever supposed to be possible?)
  • Is my issue a problem in transaction, zope.sqlalchemy, or in the application code?

What version of Python and Zope/Addons I am using:

  • transaction 2.4 -> 3.0
  • Python 3.7
  • zope.sqlalchemy 1.3
  • SQLAlchemy 1.3.20

I am going to assign this to @d-maurer as the author of the change in transaction 3.0 but maybe others have an opinion, too.

Unicode change in 2.0.3 breaking waitress

I just discovered this in the past hour. I'm running an instance of PyPiCloud via waitress on python 2.7. Any time a request is received, this is the stack trace:

ERROR 2016-11-17 21:45:52,515 [waitress] Exception when serving /
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/waitress/channel.py", line 338, in service
    task.service()
  File "/usr/local/lib/python2.7/dist-packages/waitress/task.py", line 169, in service
    self.execute()
  File "/usr/local/lib/python2.7/dist-packages/waitress/task.py", line 399, in execute
    app_iter = self.channel.server.application(env, start_response)
  File "/usr/local/lib/python2.7/dist-packages/pyramid/router.py", line 236, in __call__
    response = self.invoke_subrequest(request, use_tweens=True)
  File "/usr/local/lib/python2.7/dist-packages/pyramid/router.py", line 211, in invoke_subrequest
    response = handle_request(request)
  File "/usr/local/lib/python2.7/dist-packages/pyramid/tweens.py", line 62, in excview_tween
    reraise(*attrs['exc_info'])
  File "/usr/local/lib/python2.7/dist-packages/pyramid/tweens.py", line 22, in excview_tween
    response = handler(request)
  File "/usr/local/lib/python2.7/dist-packages/pyramid_tm/__init__.py", line 109, in tm_tween
    reraise(*exc_info)
  File "/usr/local/lib/python2.7/dist-packages/pyramid_tm/__init__.py", line 85, in tm_tween
    t.note(native_(request.path_info, 'utf-8'))
  File "/usr/local/lib/python2.7/dist-packages/transaction/_transaction.py", line 539, in note
    raise TypeError("Note must be text (unicode)")
TypeError: Note must be text (unicode)

Using version 2.0.2 works. I managed to track it down to a change made as part of the 2.0.3 release:

2.0.2...2.0.3#diff-b65460bed0b38b3a42d05ce9c3b22f1eR537

Question: Nested transaction.manager

What I did:

What would the following do:

with transaction.manager() as t1:
    # some logic
    with transaction.manager() as t2:
        # some logic

What I expect to happen:

The inner transaction.manager would create a nested transaction under t1

3.0.1: transaction not sphinx 4.0.x ready?

Looks like it is some issue with way of using sphinx

+ /usr/bin/python3 setup.py build_sphinx -b man --build-dir build/sphinx
running build_sphinx
Running Sphinx v4.0.2

Extension error:
Could not import extension repoze.sphinx.autointerface (exception: cannot import name 'force_decode' from 'sphinx.util' (/usr/lib/python3.8/site-packages/sphinx/util/__init__.py))

TypeError in traceback print on connection loss to server

I know this problem is not within the transaction module, but rather the in the stdlib, but it still may make sense to mitigate this, as this stdlib behavior is probably not going to change too soon. The setup to reproduce this is: Open a zodb server, connect a client, start a transaction, modify something, shutdown the server, try to commit the transaction.

/.../utils/database/database.py in close(self)
    366             return
    367         try:
--> 368             self.tm.commit()
    369         except transaction.interfaces.TransactionFailedError:
    370             pass

/usr/local/lib/python2.7/dist-packages/transaction/_manager.pyc in commit(self)
    109         """ See ITransactionManager.
    110         """
--> 111         return self.get().commit()
    112 
    113     def abort(self):

/usr/local/lib/python2.7/dist-packages/transaction/_transaction.pyc in commit(self)
    276             tb = None
    277             try:
--> 278                 t, v, tb = self._saveAndGetCommitishError()
    279                 self._callAfterCommitHooks(status=False)
    280                 reraise(t, v, tb)

/usr/local/lib/python2.7/dist-packages/transaction/_transaction.pyc in _saveAndGetCommitishError(self)
    298             t, v, tb = sys.exc_info()
    299             # Record how we got into commit().
--> 300             traceback.print_stack(sys._getframe(1), None, ft)
    301             # Append the stack entries from here down to the exception.
    302             traceback.print_tb(tb, None, ft)

/usr/lib/python2.7/traceback.pyc in print_stack(f, limit, file)
    268         except ZeroDivisionError:
    269             f = sys.exc_info()[2].tb_frame.f_back
--> 270     print_list(extract_stack(f, limit), file)
    271 
    272 def format_stack(f=None, limit=None):

/usr/lib/python2.7/traceback.pyc in print_list(extracted_list, file)
     23                '  File "%s", line %d, in %s' % (filename,lineno,name))
     24         if line:
---> 25             _print(file, '    %s' % line.strip())
     26 
     27 def format_list(extracted_list):

/usr/lib/python2.7/traceback.pyc in _print(file, str, terminator)
     11 
     12 def _print(file, str='', terminator='\n'):
---> 13     file.write(str+terminator)
     14 
     15 

TypeError: 'unicode' does not have the buffer interface

Here str is a unicode line from the stack trace. Though I'm not totally sure why it is unicode at all.

It's very easy to create reference cycles that require the GC to clean up

As discussed in zopefoundation/ZODB#268 (comment), if, through any arbitrarily long chain of imports, a data manager implementation ultimately winds up importing the transaction module, then when it joins the default transaction there will be a reference cycle (the data manager will reference the transaction module through __globals__, which references the transaction manager, which references the transaction which references the data manager).

I'm not sure what, if anything, can be done about this, but if anybody has an idea, that'd be great.

3.0.1: pytest warnings

BUG/PROBLEM REPORT (OR OTHER COMMON ISSUE)

What I did:

What I expect to happen:

What actually happened:

What version of Python and Zope/Addons I am using:

Transaction manager run problems

  1. When the number of retries has been exceeded, we don't abort the transaction. We should always abort and then raise.

  2. The run implementation doesn't account for application code creating new transactions. This is an important use case. Consider an application that catches errors, and needs to record information about the errors transactionally. In this case, the application aborts the current transaction, uses a new transaction to record some information, or otherwise recover. The run implementation should use the transaction manager methods to do it's work, rather than assuming it can interact with the initial transaction. Currently, an error is raised if the transaction changes.

Transactions with multiprocessing

Hello, can I somehow use transactions with shared transaction.manager from multiple processes?

For example, I have two processes incrementing counter in for loop:

def worker():
    for i in range(0, 10000):
        counter.value += 1

And I want commit result of incrementation by transaction like this:

def worker():
    with transaction.manager:
        for i in range(0,10000):
            counter.value += 1
        transaction.commit()

But I think this code creates local transaction.manager for each process. Can I somehow share it?

"Transaction.abort" should abort the resources in the inverse order then "commit" commits the resources

The order in which resources are committed during a commit can be important. For this reason, this order can be influenced by defining a method sortKey on the resources. For abort, the resources are aborted in registration order - independent of sortKey.

However, the order in which resources are aborted can be important as well. In my case, a Plone portal should be created. During the operation, a ZODB connection and various collective.indexing.transactions.QueueTM objects register with the transaction (in this order). An error during the portal creation causes a transaction.abort() which fails, because the connection abort ghostifies the portal's component registry and the collective.indexing.transactions.QueueTM abort depends on it.

It would be good to have control not only over the commit order but also over the abort order of resources. I assume that aborting resources in the inverse order of committing them would most likely give acceptable results.

add a public api for tracking attempts

The manager.run(attempts) api is awesome. I'd like to use it in pyramid_tm which currently re-implements most of the logic itself. There's one feature that would be really useful which is a public api on the transaction (or the manager) to expose what's happening to underlying code.

Specifically we have some cases where we'd like to do things differently on the last attempt than on earlier attempts that raise retryable exceptions and there's no way to know that we're on the last attempt using the current run api.

I'd propose something like txn.is_last_attempt or txn.attempt_index combined with txn.max_attempts or txn.attempts_remaining. This would allow underlying code to make decisions on what it does with retryable exceptions.

I'm happy to work on a PR for this but wanted to get some input on the API first. Personally I prefer txn.is_last_attempt as I don't have an explicit need to expose the exact number of attempts to underlying code.

'ThreadTransactionManager' object has no attribute '_retryable'

Hello,
Since upgrade to version 2.4, i have the following error "'ThreadTransactionManager' object has no attribute '_retryable'". I'm using pyramid, sqlalchemy and pyramid_tm. The problem occurs in my functional tests where I use ThreadTransactionManager instead of Zope transaction manager.
I think that the function "_retryable" has been removed in this pull request #68
Is it a bug or a mistake in the transaction lib or should I modify some thing in my sqlalchemy configuration ?
Thanks,
Julien

API to access the status of a transaction

Currently there is isDoomed() to check whether a transaction is doomed.
But there are no methods to check the other status (like Active or Commit failed).
To check for these status it is currently necessary to do something like this:

from transaction._transaction import Status
if transaction.get().status == Status.ACTIVE:
    pass

It would be nice to

  • either have methods on the transaction for the status checks
  • or a way to access the Status constants without importing from _transaction (which feels like using internals instead of the API).

What do you think?

Support abort hooks (symmetrically to commit hooks)

transaction currently supports (before and after) commit hooks. It should symmetrically support abort hooks as well.

I need this feature in dm.zodb.asynchronous, more precisely in its TransactionalScheduler. This scheduler allows to schedule a function call at transaction commit; it uses a commit hook for this. If the transaction is aborted (instead of committed), a cleanup becomes necessary. This requires a (transaction) abort hook.

I implement this feature currently in dm.transaction.aborthook via monkey patching. If there is interest, I could implement it instead in transaction.

Question: How to find a transaction’s data manager, if any?

What is the recommended way to check whether a custom data manager has joined a transaction’s set of registered data managers?

Currently I am using a transaction’s extensions dictionary, but that feels somewhat flimsy:

tm = transaction.manager  # Get the thread-local transaction manager.
try:
    tx = tm.get()
except Exception:  # transaction.interfaces.NoTransaction# No transaction available, do something.
else:
    if "SuperDataManager" not in tx.extension:
        # See also https://github.com/zopefoundation/transaction/issues/47
        dm = SuperDataManager(tm)
        tx.join(dm)
        tx.setExtendedInfo("SuperDataManager", dm)
    else:
        dm = tx.extension["SuperDataManager"] 
    dm.queue_my_data(some_data)
finally:
    pass

It seems to me that data managers are stored in a private list:

self._resources = []

and checking whether a given data manager exists might be as simple as

def get_joined(data_manager_type):
    """
    Return the data manager of type `data_manager_type` if it has joined this transaction, 
    or None if it hasn’t.
    """
    return next((_ for _ in self._resources if isinstance(_, data_manager_type)), None)

which would simplify the above code:

dm = tx.get_joined(SuperDataManager)
if not dm:
    # See also https://github.com/zopefoundation/transaction/issues/47
    dm = SuperDataManager(tm)
    tx.join(dm)
dm.queue_my_data(some_data)

If I was to make the SuperDataManager thread-local like the default transaction manager, then I could also compare by the dm’s object id() directly.

Documentation is unhelpful for complete newcomers

I’ve tried to figure out whether transaction could help me in one of my projects, and the documentation left me completely baffled. It seems to be mostly a bunch of maintainer notes?

Things I noticed as a first-time reader:

  • The first sentence is “Transaction objects manage resources for an individual activity.” – that doesn’t tell me any more than the package name.
  • The first section is a ZOPE compatibility notice. I guess at this point you lose 90% of readers because it makes the lib look like a ZOPE implementation detail.
  • Next some low-level concepts are outlined without any explanation how they fit together.
  • And with that you’re thrown into the meat of the documentation that doesn’t make any sense whatsoever without understanding what the lib is about and how things fit together.
  • Especially egregious are the chapters on how to write DataManagers and ResourceManagers without explaining what they are (I still have no clue) or why I’d want to implement them.
    • Bonus points for saying This transaction.tests.examples.DataManager class provides a trivial data-manager implementation and docstrings to illustrate the the protocol and to provide a tool for writing tests. while there isn’t a single docstring beyond Used by the 'datamanager' chapter in the Sphinx docs.. :)
    • it would also be nice to link to it

All that to say is that I care because I know that most of the zope libraries a best of breed and it would be a pity if people re-implemented their functionality (poorly), just because they couldn’t figure out how to use them.

I understand this issue is not just one issue, so feel free to handle however you want it.

Joining a committing transaction

Hi, I stumbled upon the following comment when digging around as to why I was getting an exception:

            # TODO: Should it be possible to join a committing transaction?
            # I think some users want it.

I don't know if this is the appropriate forum for this, but I am a user who wants this. I can elaborate on my use case if it matters.

3.0.1: setuptools build_sphinx command fails

AFAIK it is not possible to specify in setup.cfg in [build_sphinx] section base path to module.
If I'm right this could be solved by move src/transaction/ to transaction/

+ /usr/bin/python3 setup.py build_sphinx -b man
running build_sphinx
Running Sphinx v4.0.2

Configuration error:
There is a programmable error in your configuration file:

Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/sphinx/config.py", line 323, in eval_config_file
    exec(code, namespace)
  File "/home/tkloczko/rpmbuild/BUILD/transaction-3.0.1/docs/conf.py", line 22, in <module>
    rqmt = pkg_resources.require('transaction')[0]
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 901, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 787, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'transaction' distribution was not found and is required by the application

And after copy src/transaction* to root directory

[tkloczko@barrel transaction-3.0.1]$ /usr/bin/python3 setup.py build_sphinx -b man
running build_sphinx
Running Sphinx v4.0.2

Extension error:
Could not import extension repoze.sphinx.autointerface (exception: No module named 'repoze')

And here is another esmall problem because repoze module requires on testing transaction and transaction requires on building documentation repoze.sphinx.autointerface (which is part of repoze).
It is kind of cyrcular dependency :/
IMO it would be good to rewrite transaction documentation using only plain sphinx.

need a simple api to detect an active transaction

manager.begin() will automatically abort an active transaction. The only way to determine if one is active is via manager._txn is None. This private api is the only way right now to write a function that starts a new transaction only if one does not already exist. Similarly manager.get() will create a transaction if one does not exist.

manager.is_active() maybe?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.