Giter VIP home page Giter VIP logo

zodbupdate's Introduction

zodbupdate - Update existing databases to match your software

This package provides a tool that automatically identifies and updates references from persistent objects to classes that are in the process of being moved from one module to another and/or being renamed.

If a class is being moved or renamed, you need to update all references from your database to the new name before finally deleting the old code.

This tool looks through all current objects of your database, identifies moved/renamed classes and touches objects accordingly. It creates transactions that contains the update of your database (one transaction every 100,000 records).

Having run this tool, you are then free to delete the old code.

Installing the egg of this tool provides a console script zodbupdate which you can call giving either a FileStorage filename or a configuration file defining a storage:

$ zodbupdate -f Data.fs
$ zodbupdate -c zodb.conf

Detailed usage information is available:

$ zodbupdate -h

It is important to install this egg in an interpreter/environment where your software is installed as well. If you're using a regular Python installation or virtualenv, just installing the package using easy_install should be fine.

If you are using buildout, installing can be done using the egg recipe with this configuration:

[buildout]
parts += zodbupdate

[zodbupdate]
recipe = zc.recipe.egg
eggs = zodbupdate
    <list additional eggs here>

If you do not install zodbupdate together with the necessary software it will report missing classes and not touch your database.

You can configure any storage known to your ZODB installation by providing a ZConfig configuration file (similar to zope.conf). For example you can connect to a ZEO server by providing a config file zeo.conf:

<zeoclient>
    server 127.0.0.1:8100
    storage 1
</zeoclient>

And then running zodbupdate using:

$ zodbupdate -c zeo.conf

Rename rules can be defined using an entry point called zodbupdate:

setup(...
      entry_points = """
      [zodbupdate]
      renames = mypackage.mymodule:rename_dict
      """)

These can also be defined in python:

setup(...
      entry_points={
        'zodbupdate': ['renames = mypackage.mymodule:rename_dict'],
      })

Those entry points must points to dictionaries that map old class names to new class names:

rename_dict = {
    'mypackage.mymodule ClassName':
    'otherpackage.othermodule OtherClass'}

As soon as you have rules defined, you can already remove the old import location mentioned in them.

The option --pack will pack the storage on success. (You tell your users to use that option. If they never pack their storage, it is a good occasion).

zodbupdate can be used to migrate a database created with a Python 2 application to be usable with the same application in Python 3. To accomplish this, you need to:

  1. Stop your application. Nothing should be written to the database while the migration is running.
  2. Update your Python 2 application to use the latest ZODB version. It will not work with ZODB 3.
  3. With Python 2, run zodbupdate --pack --convert-py3.

If you use a Data.fs we recommend you to use the -f option to specify your database. After the conversion the magic header of the database will be updated so that you will be able to open the database with Python 3.

If you use a different storage (like RelStorage), be sure you will be connecting to it using your Python 3 application after the migration. You will still be able to connect to your database and use your application with Python 2 without errors, but then you will need to convert it again to Python 3.

While the pack is not required, it is highly recommended.

The conversion will take care of the following tasks:

  • Updating stored Python datetime, date and time objects to use Python 3 bytes,
  • Updating ZODB references to use Python 3 bytes.
  • Optionally convert stored strings to either unicode or bytes pending your configuration.

If your application expect to use bytes in Python 3, they must be stored as such in the database, and all other strings must be stored as unicode string, if they contain other characters than ascii characters.

When using --convert-py3, zodbupdate will load a set of decoders from the entry points:

setup(...
      entry_points = """
      [zodbupdate.decode]
      decodes = mypackage.mymodule:decode_dict
      """)

Decoders are dictionaries that specifies as keys attributes on Persistent classes that must either be encoded as bytes (if the value is binary) or decoded to unicode using value as encoding (for instance utf-8 here):

decode_dict = {
   'mypackage.mymodule ClassName attribute': 'binary',
   'otherpackage.othermodule OtherClass other_attribute': 'utf-8'}

Please note that for the moment only attributes on Persistent classes are supported.

Please also note that these conversion rules are _only_ selected for the class that is referenced in the pickle, rules for superclasses are _not_ applied. This means that you have to push down annotation rules to all the subclasses of a superclass that has a field that needs this annotation.

zodbupdate can also be run from within Python 3 to convert a database created with Python 2 to be usable in Python 3. However this works slightly differently than when running the conversion using Python 2. In Python 3 you must specify a default encoding to use while unpickling strings: zodbupdate --pack --convert-py3 --encoding utf-8.

For each string in the database, zodbupdate will convert it as follows:

  1. If it's an attribute configured explicitly via a decoder as described above, it will be decoded or encoded as specified there.
  2. Otherwise the value will be decoded using the encoding specified on the command line.
  3. If there is an error while decoding using the encoding specified on the command line, the value will be stored as bytes.

If you call zodbupdate with -f and the path to your Data.fs, records triggering those errors will be ignored.

We recommend to run zodbupdate with -v -d to get the maximum of information.

If you are working on big storages, you can use the option -o to re-run zodbupdate at a failing record you previously encountered afterward.

zodbupdate's People

Contributors

dataflake avatar davisagli avatar dwt avatar faassen avatar frisi avatar icemac avatar jamadden avatar jaroel avatar jensens avatar jugmac00 avatar mauritsvanrees avatar mgedmin avatar pbauer avatar rbu avatar tflorac avatar thefunny42 avatar

Stargazers

 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zodbupdate's Issues

zodbupdate --convert-py3 writes a bad index file

I've noticed that zodbupdate -f Data.fs --pack --convert-py3 (on Python 2) writes a Data.fs.index that is then not accepted by ZODB's FileStorage on Python 3, giving me an error:

2019-07-24T13:52:16 ERROR ZODB.FileStorage loading index
Traceback (most recent call last):
  File "/home/mg/.buildout/eggs/ZODB-5.5.1-py3.5.egg/ZODB/FileStorage/FileStorage.py", line 465, in _restore_index
    info = fsIndex.load(index_name)
  File "/home/mg/.buildout/eggs/ZODB-5.5.1-py3.5.egg/ZODB/fsIndex.py", line 134, in load
    v = unpickler.load()
UnicodeDecodeError: 'ascii' codec can't decode byte 0x82 in position 83: ordinal not in range(128)

Perhaps the conversion script should remove the useless index file if it cannot write a suitable one?

zodbupdate --convert-to-py3 fails to handle an IOBucket mapping ints to bytestrings

I'm trying to migrate a Zope 3 application to Python 3. The Data.fs is about 1.2 gigs and has about 3 million objects in it. The procedure is

virtualenv -p python2 env && env/bin/pip install zodbupdate -e ~/src/myapp
env/bin/zodbupdate -f Data.fs --pack --convert-py3

and then I'm doing a dry-run on Python 3 to validate that all the pickles can be loaded

virtualenv -p python3 env3 && env3/bin/pip install zodbupdate -e ~/src/myapp
env3/bin/zodbupdate -v -n -f Data.fs

which fails on OID 0x2db225, which is a BTrees.IOBTree.IOBucket instance. It's not the first IOBucket in the database, but it's the first one that cannot be converted.

The error is in the unpickler and it is a

Traceback (most recent call last):
  File "/home/mg/Private/ivija-staging/env3/lib/python3.5/site-packages/zodbupdate/main.py", line 214, in main
    updater()
  File "/home/mg/Private/ivija-staging/env3/lib/python3.5/site-packages/zodbupdate/update.py", line 82, in __call__
    new = self.processor.rename(current)
  File "/home/mg/Private/ivija-staging/env3/lib/python3.5/site-packages/zodbupdate/serialize.py", line 333, in rename
    data = unpickler.load()
UnicodeDecodeError: 'ascii' codec can't decode byte 0x81 in position 0: ordinal not in range(128)

The raw pickle data, post conversion, looks like this:

    0: \x80 PROTO      3
    2: c    GLOBAL     'BTrees.IOBTree IOBucket'
   27: q    BINPUT     1
   29: .    STOP
highest protocol among opcodes = 2

   30: \x80 PROTO      3
   32: (    MARK
   33: J        BININT     231506130
   38: U        SHORT_BINSTRING '\x81x\x84Y'
   44: q        BINPUT     2
   46: J        BININT     231506137
   51: U        SHORT_BINSTRING '¾\x84`'
   56: q        BINPUT     3
   58: J        BININT     231506138
   63: U        SHORT_BINSTRING 'â\x84a'
   68: q        BINPUT     4
   70: J        BININT     396470070
   75: U        SHORT_BINSTRING '\x88Z\x88['
   81: q        BINPUT     5
   83: J        BININT     489281365
   88: U        SHORT_BINSTRING '\x88U\x88O'
   94: q        BINPUT     6
   96: J        BININT     1074075642
  101: U        SHORT_BINSTRING '\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b'
  114: q        BINPUT     7
...

On Python 2 the raw pickle looks like

    0: c    GLOBAL     'BTrees.IOBTree IOBucket'
   25: q    BINPUT     1
   27: .    STOP
highest protocol among opcodes = 1

   28: (    MARK
   29: (        MARK
   30: J            BININT     231506130
   35: U            SHORT_BINSTRING '\x81x\x84Y'
   41: q            BINPUT     2
   43: J            BININT     231506137
   48: U            SHORT_BINSTRING '\xbe\x84`'
   53: q            BINPUT     3
   55: J            BININT     231506138
   60: U            SHORT_BINSTRING '\xe2\x84a'
   65: q            BINPUT     4
   67: J            BININT     396470070
   72: U            SHORT_BINSTRING '\x88Z\x88['
   78: q            BINPUT     5
   80: J            BININT     489281365
   85: U            SHORT_BINSTRING '\x88U\x88O'
   91: q            BINPUT     6
   93: J            BININT     1074075642
   98: U            SHORT_BINSTRING '\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b'
  111: q            BINPUT     7
...

which gets unpickled into a tuple

(
(231506130,
'\x81x\x84Y',
231506137,
'\xbe\x84`',
231506138,
'\xe2\x84a',
396470070,
'\x88Z\x88[',
489281365,
'\x88U\x88O',
1074075642,
'\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b',
...
), <persistent reference to the next IOBucket>)

I have no idea what this IOBTree contains, but it looks like it's mapping ints to bytestrings.

This would be a perfect opportunity to use a custom decode annotation and mark the attribute as binary, except there's no attribute here. IOBucket.__getstate__ returns that tuple, and not a dict mapping attributes to values.

What would be a good way to have zodbupdate --convert-py3 deal with this kind of situation?

Conversion fails on broken pickles and zlib decompression errors

This is not really a bug report, as I understand, that it's probably out of scope for zodbupdate to handle all kinds of data corruption within a zodb file. I was unlucky enough, to have to convert a rather old, large and grown py2 zodb to py3, and unfortunately zodbupdate didn't work, as a handful of objects within the DB were broken for different reasons with different errors.

To save some other poor souls hours of grief, I'd like to share the patched version of zodbupdate I came up with in the end. Sorry for using this bugtracker, but it's probably the spot where people would look for first, if they'd run into the same issues.

The patched version catches upcoming errors while iterating all data records, treats the errors as good as possible, and then just goes on instead of crashing. If you consider this to be an option for zodbupdate in a more general sense, feel free to shamelessly copy whatever you consider useful. Here the tool:

https://gist.github.com/ml31415/b4d00251e76afe35358070564864287a

Add analyisis script to zodbmigrate

Currently you need to use https://pypi.org/project/zodb.py3migrate/ to analyze existing objects in the ZODB and list classes with missing [zodbupdate.decode] mapping for attributes containing string values that could possibly break when converted to python3.

bin/zodb-py3migrate-analyze py2/var/filestorage/Data.fs -b py2/var/blobstorage -v

We might consider to put this functionality into zodbupdate so people dealing with python3 migration of their database do not need to setup/use a second tool.

AttributeError: 'Transaction' object has no attribute 'extension_bytes' [was: Tests in TravisCI are broken]

All jobs besides the OLDZODB one are broken, e. g. https://travis-ci.org/github/zopefoundation/zodbupdate/jobs/701272511

There are multiple test failures which all look like this:

Traceback (most recent call last):
264  File "/opt/python/3.8.0/lib/python3.8/unittest/case.py", line 60, in testPartExecutor
265    yield
266  File "/opt/python/3.8.0/lib/python3.8/unittest/case.py", line 676, in run
267    self._callTestMethod(testMethod)
268  File "/opt/python/3.8.0/lib/python3.8/unittest/case.py", line 633, in _callTestMethod
269    method()
270  File "/home/travis/build/zopefoundation/zodbupdate/src/zodbupdate/tests.py", line 871, in test_convert_attribute_to_bytes
271    self.update(convert_py3=True, default_decoders={
272  File "/home/travis/build/zopefoundation/zodbupdate/src/zodbupdate/tests.py", line 168, in update
273    updater()
274  File "/home/travis/build/zopefoundation/zodbupdate/src/zodbupdate/update.py", line 78, in __call__
275    t = self.__new_transaction()
276  File "/home/travis/build/zopefoundation/zodbupdate/src/zodbupdate/update.py", line 59, in __new_transaction
277    self.storage.tpc_begin(t)
278  File "/home/travis/virtualenv/python3.8.0/lib/python3.8/site-packages/ZODB/BaseStorage.py", line 193, in tpc_begin
279    ext = transaction.extension_bytes
280AttributeError: 'Transaction' object has no attribute 'extension_bytes'

Do we use a too new or too old version of transaction?
The tests break locally in the same way.
There is even no extension_bytes in the transaction package.
Am I missing something here?

Add test script for py3 migration

After running python2 bin/zopeupdate --convert-py3 it would make sense to check if all database records can be unpickled w/o errors under python3

so run `zodbupdate --test-convert-py3``under python3 and list all oids, serials, classnames for pickles that lead to a UnicodeDecoreError.

@thefunny42 already did something similar that we can adapt and reuse in mdtools.relstorage: https://github.com/minddistrict/mdtools.relstorage/tree/master/src/mdtools/relstorage/search.py

Inability to declare conversion rules on superclasses

Hi there,

yesterday we noticed that it is not possible to declare a conversion rule once for a class that has an instance variable, but that it instead has to be declared for all subclasses that inherit from that class that are actually in the the pickle.

On the one hand this makes sense as the tool (at that stage) doesn't know anything about the classes, doesn't instantiate them and just works on them, but on the other hand the projects where those classes are defined need to be installed so the rules can be found via the entry points defined in those projects.

In principle it would thus be possible to look at the MRO of all loadable classes and push down all the conversion rules from all the superclasses to all their leaf classes that are found.

However, even if this is not done, it should be documented that you need to push down these annotations to all the leaf classes that are actually reference in the pickle files for them to work. (I'm working on a pull request for this currently)

UnicodeDecodeError and AssertionError

Using rb3b31b0db70ebad533cc5b66e0d480d8a3c9b874 for a Plone in-place migration.

However this fails badly under Python 3.7.5:

Updating magic marker for var/filestorage/Data.fs
Loaded 1 rename rules from OFS:renames
Loaded 2 decode rules from AccessControl:decodes
Loaded 13 decode rules from OFS:decodes
Loaded 3 decode rules from Products.PythonScripts:decodes
Loaded 1 decode rules from Products.ZopeVersionControl:decodes
An error occured
Traceback (most recent call last):
  File "/home/ajung/sandboxes/dynamore-plone-5.2-upgrade/src/zodbupdate/src/zodbupdate/main.py", line 221, in main
    updater()
  File "/home/ajung/sandboxes/dynamore-plone-5.2-upgrade/src/zodbupdate/src/zodbupdate/update.py", line 82, in __call__
    new = self.processor.rename(current)
  File "/home/ajung/sandboxes/dynamore-plone-5.2-upgrade/src/zodbupdate/src/zodbupdate/serialize.py", line 333, in rename
    data = unpickler.load()
UnicodeDecodeError: 'ascii' codec can't decode byte 0xfd in position 6: ordinal not in range(128)
Stopped processing, due to: 'ascii' codec can't decode byte 0xfd in position 6: ordinal not in range(128)
Traceback (most recent call last):
  File "/home/ajung/sandboxes/dynamore-plone-5.2-upgrade/src/zodbupdate/src/zodbupdate/main.py", line 221, in main
    updater()
  File "/home/ajung/sandboxes/dynamore-plone-5.2-upgrade/src/zodbupdate/src/zodbupdate/update.py", line 82, in __call__
    new = self.processor.rename(current)
  File "/home/ajung/sandboxes/dynamore-plone-5.2-upgrade/src/zodbupdate/src/zodbupdate/serialize.py", line 333, in rename
    data = unpickler.load()
UnicodeDecodeError: 'ascii' codec can't decode byte 0xfd in position 6: ordinal not in range(128)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "bin/zodbupdate", line 304, in <module>
    sys.exit(zodbupdate.main.main())
  File "/home/ajung/sandboxes/dynamore-plone-5.2-upgrade/src/zodbupdate/src/zodbupdate/main.py", line 225, in main
    raise AssertionError()
AssertionError

Ideas for migrating `sets.Set()` instances to `builtins.set()` during py3 migration?

I've a Data.fs that was originally created in 2005. It contains, among other things, instances of sets.Set(). These things obviously do not exist on Python 3. I though maybe I could get away with a rename_dict entry of

 'sets Set': 'builtins set',

but unfortunately the pickled data structure differs sufficiently so all this does is create an unpickling error.

I'm not entirely sure how to approach the problem. I think I need a custom sets.Set subclass with a custom __reduce__, a la zodbupdate.convert.Datetime and friends? If I manage to write one, I'll submit a pull request.

Allow arbitrary default-encoding via commandline param

plonesites should use utf-8 at most places, but here in halle we heard about other companies that are using latin-1 as default encoding.
to support migrating these datbases i suggest to add a command line option used combined with --convert-py that allows to tell zodbupdate which encoding to use when converting python2 str to python3 str:

zodbupdate --convert-py --default-encoding='latin-1'

actually it would also be better to rename the marker for python 3 str (formerly python2 unicode) from utf-8 to something that else as it might also stand for latin-1 or other encodings:

#old:
decode_dict = {
   'mypackage.mymodule ClassName attribute': 'binary',
   'otherpackage.othermodule OtherClass other_attribute': 'utf-8'}

#new:
decode_dict = {
   'mypackage.mymodule ClassName attribute': 'binary',
   'otherpackage.othermodule OtherClass other_attribute': 'encoded_str'}

Running with RelStorage?

Hi,
I've tested zodbupdate (1.0.0 and 1.4.0) on a database using RelStorage 3.0.1.
But I get an exception when running:

Loaded 3 rename rules from pyams_site.generations:renames
Loaded 2 rename rules from pyams_catalog.generations:renames
An error occured
Traceback (most recent call last):
  File "/var/local/env/pycharm/lib/python3.5/site-packages/zodbupdate/main.py", line 186, in main
    updater()
  File "/var/local/env/pycharm/lib/python3.5/site-packages/zodbupdate/update.py", line 97, in __call__
    raise error
  File "/var/local/env/pycharm/lib/python3.5/site-packages/zodbupdate/update.py", line 74, in __call__
    for oid, serial, current in self.records:
  File "/var/local/env/pycharm/lib/python3.5/site-packages/zodbupdate/update.py", line 140, in records
    not self.storage.supportsUndo()):
AttributeError: 'RelStorage' object has no attribute 'supportsUndo'
Stopped processing, due to: 'RelStorage' object has no attribute 'supportsUndo'

So is there a way to update a RelStorage database without converting it back and forth into a classic FileStorage?

Best regards,
Thierry

UserWarning

about to run tox for zopefoundation/zodbupdate, 263 of 287
warning: no files found matching '.coveragerc'
warning: no files found matching 'versions.cfg'
warning: no files found matching '.coveragerc'
warning: no files found matching 'versions.cfg'
/home/jugmac00/All/output_zope/zopefoundation/zodbupdate/.tox/4/py36/lib/python3.6/site-packages/relstorage/storage/__init__.py:610: UserWarning: There is no defined value for the *next* parameter. RelStorage will rely on implementation-defined behaviour and treat it like FileStorage does, while assuming that iteration is beginning from the start. This may change in the future.
  "There is no defined value for the *next* parameter. "
tox4 run successful for zopefoundation/zodbupdate

Disclaimer: I do not use this package.

AttributeError: 'ZODBReference' object has no attribute '__bases__'

my zodbupdate raise the follwoing error:

Traceback (most recent call last):
  File "/Development/Plone/coredev52multipy/py3/eggs/zope.interface-4.6.0-py3.7-linux-x86_64.egg/zope/interface/declarations.py", line 269, in implementedByFallback
    bases = cls.__bases__
AttributeError: 'ZODBReference' object has no attribute '__bases__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Development/Plone/coredev52multipy/py3/eggs/zodbupdate-1.2-py3.7.egg/zodbupdate/main.py", line 214, in main
    updater()
  File "/Development/Plone/coredev52multipy/py3/eggs/zodbupdate-1.2-py3.7.egg/zodbupdate/update.py", line 82, in __call__
    new = self.processor.rename(current)
  File "/Development/Plone/coredev52multipy/py3/eggs/zodbupdate-1.2-py3.7.egg/zodbupdate/serialize.py", line 333, in rename
    data = unpickler.load()
  File "/Development/Plone/coredev52multipy/py3/eggs/zope.interface-4.6.0-py3.7-linux-x86_64.egg/zope/interface/declarations.py", line 272, in implementedByFallback
    raise TypeError("ImplementedBy called for non-factory", cls)
TypeError: ('ImplementedBy called for non-factory', <zodbupdate.serialize.ZODBReference object at 0x7fe45a0fb5c0>)
Stopped processing, due to: ('ImplementedBy called for non-factory', <zodbupdate.serialize.ZODBReference object at 0x7fe45a0fb5c0>)
Traceback (most recent call last):
  File "/Development/Plone/coredev52multipy/py3/eggs/zope.interface-4.6.0-py3.7-linux-x86_64.egg/zope/interface/declarations.py", line 269, in implementedByFallback
    bases = cls.__bases__
AttributeError: 'ZODBReference' object has no attribute '__bases__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Development/Plone/coredev52multipy/py3/eggs/zodbupdate-1.2-py3.7.egg/zodbupdate/main.py", line 214, in main
    updater()
  File "/Development/Plone/coredev52multipy/py3/eggs/zodbupdate-1.2-py3.7.egg/zodbupdate/update.py", line 82, in __call__
    new = self.processor.rename(current)
  File "/Development/Plone/coredev52multipy/py3/eggs/zodbupdate-1.2-py3.7.egg/zodbupdate/serialize.py", line 333, in rename
    data = unpickler.load()
  File "/Development/Plone/coredev52multipy/py3/eggs/zope.interface-4.6.0-py3.7-linux-x86_64.egg/zope/interface/declarations.py", line 272, in implementedByFallback
    raise TypeError("ImplementedBy called for non-factory", cls)
TypeError: ('ImplementedBy called for non-factory', <zodbupdate.serialize.ZODBReference object at 0x7fe45a0fb5c0>)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./py3/bin/zodbupdate", line 265, in <module>
    sys.exit(zodbupdate.main.main())
  File "/Development/Plone/coredev52multipy/py3/eggs/zodbupdate-1.2-py3.7.egg/zodbupdate/main.py", line 218, in main
    raise AssertionError()
AssertionError

Any hints?

Unexpected failure converting in Python 3 when using dry run

The README states that it is possible to do a one-step conversion of a ZODB created under Python 2 to a Python 3 compatible format. One important caveat is missing here, though. This does not apply to "older" ZODB files coming from, say, Zope 2.13.

The issue is the Data.fs magic number. Older Data.fs files will be FS21, which the ZODB code under Python 3 completely refuses to open. They open fine with the same ZODB code under Python 2.

I do not know if this is intended behavior in zodbupdate and just needs documentation, or if it is a bug where e.g. zodbupdate could rewrite the magic number before the ZODB code attempts to open the file.

This is already causing confusion, see zopefoundation/Zope#661.

Test RelStorage in CI?

With RelStorage 3.0's support for sqlite3 databases, it's possible to use RelStorage without having a RDBMS server running and no major additional dependencies. It might be nice if RelStorage was tested here in addition to FileStorage to catch things like #28.

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.