Skip to content

Commit 02b65d7

Browse files
committed
Factor common code to hyperdb/roundupdb
There was some common copied code in backends/back_anydbm.py and backends/rdbms_common.py. This is now moved to hyperdb.py and roundupdb.py, respectively (the FileClass lives in hyperdb.py while the IssueClass is in roundupdb.py)
1 parent 44dfa19 commit 02b65d7

File tree

4 files changed

+193
-330
lines changed

4 files changed

+193
-330
lines changed

roundup/backends/back_anydbm.py

Lines changed: 9 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import time
3232

3333
from roundup.anypy.dbm_ import anydbm, whichdb
34-
from roundup.anypy.strings import b2s, bs2b, repr_export, eval_import, is_us
34+
from roundup.anypy.strings import b2s, repr_export, eval_import, is_us
3535

3636
from roundup import hyperdb, date, password, roundupdb, security, support
3737
from roundup.mlink_expr import Expression, ExpressionError
@@ -48,8 +48,6 @@
4848

4949
from roundup.backends.indexer_common import get_indexer
5050

51-
from hashlib import md5
52-
5351

5452
def db_exists(config):
5553
# check for the user db
@@ -2221,168 +2219,19 @@ def export_journals(self):
22212219

22222220

22232221
class FileClass(hyperdb.FileClass, Class):
2224-
"""This class defines a large chunk of data. To support this, it has a
2225-
mandatory String property "content" which is typically saved off
2226-
externally to the hyperdb.
2227-
2228-
The default MIME type of this data is defined by the
2229-
"default_mime_type" class attribute, which may be overridden by each
2230-
node if the class defines a "type" String property.
2231-
"""
2222+
# Use for explicit upcalls in generic code, for py2 compat we cannot
2223+
# use super() without making everything a new-style class.
2224+
subclass = Class
22322225
def __init__(self, db, classname, **properties):
2233-
"""The newly-created class automatically includes the "content"
2234-
and "type" properties.
2235-
"""
2236-
if 'content' not in properties:
2237-
properties['content'] = hyperdb.String(indexme='yes')
2238-
if 'type' not in properties:
2239-
properties['type'] = hyperdb.String()
2226+
self._update_properties(properties)
22402227
Class.__init__(self, db, classname, **properties)
22412228

2242-
def create(self, **propvalues):
2243-
""" Snarf the "content" propvalue and store in a file
2244-
"""
2245-
# we need to fire the auditors now, or the content property won't
2246-
# be in propvalues for the auditors to play with
2247-
self.fireAuditors('create', None, propvalues)
2248-
2249-
# now remove the content property so it's not stored in the db
2250-
content = propvalues['content']
2251-
del propvalues['content']
2252-
2253-
# make sure we have a MIME type
2254-
mime_type = propvalues.get('type', self.default_mime_type)
2255-
2256-
# do the database create
2257-
newid = self.create_inner(**propvalues)
2258-
2259-
# store off the content as a file
2260-
self.db.storefile(self.classname, newid, None, bs2b(content))
2261-
2262-
# fire reactors
2263-
self.fireReactors('create', newid, None)
2264-
2265-
return newid
2266-
2267-
def get(self, nodeid, propname, default=_marker, cache=1):
2268-
""" Trap the content propname and get it from the file
2269-
2270-
'cache' exists for backwards compatibility, and is not used.
2271-
"""
2272-
poss_msg = 'Possibly an access right configuration problem.'
2273-
if propname == 'content':
2274-
try:
2275-
return b2s(self.db.getfile(self.classname, nodeid, None))
2276-
except IOError as strerror:
2277-
# XXX by catching this we don't see an error in the log.
2278-
return 'ERROR reading file: %s%s\n%s\n%s' % (
2279-
self.classname, nodeid, poss_msg, strerror)
2280-
except UnicodeDecodeError as e:
2281-
# if content is not text (e.g. jpeg file) we get
2282-
# unicode error trying to convert to string in python 3.
2283-
# trap it and supply an error message. Include md5sum
2284-
# of content as this string is included in the etag
2285-
# calculation of the object.
2286-
return ('%s%s is not text, retrieve using '
2287-
'binary_content property. mdsum: %s') % (
2288-
self.classname, nodeid,
2289-
md5(self.db.getfile(
2290-
self.classname, nodeid, None)).hexdigest()) # nosec - bandit md5 use ok
2291-
elif propname == 'binary_content':
2292-
return self.db.getfile(self.classname, nodeid, None)
2293-
2294-
if default is not _marker:
2295-
return Class.get(self, nodeid, propname, default)
2296-
else:
2297-
return Class.get(self, nodeid, propname)
2298-
2299-
def set(self, itemid, **propvalues):
2300-
""" Snarf the "content" propvalue and update it in a file
2301-
"""
2302-
self.fireAuditors('set', itemid, propvalues)
2303-
2304-
# create the oldvalues dict - fill in any missing values
2305-
oldvalues = copy.deepcopy(self.db.getnode(self.classname, itemid))
2306-
for name, prop in self.getprops(protected=0).items():
2307-
if name in oldvalues:
2308-
continue
2309-
if isinstance(prop, hyperdb.Multilink):
2310-
oldvalues[name] = []
2311-
else:
2312-
oldvalues[name] = None
2313-
2314-
# now remove the content property so it's not stored in the db
2315-
content = None
2316-
if 'content' in propvalues:
2317-
content = propvalues['content']
2318-
del propvalues['content']
2319-
2320-
# do the database update
2321-
propvalues = self.set_inner(itemid, **propvalues)
2322-
2323-
# do content?
2324-
if content:
2325-
# store and possibly index
2326-
self.db.storefile(self.classname, itemid, None, bs2b(content))
2327-
if self.properties['content'].indexme:
2328-
index_content = content
2329-
if bytes != str and isinstance(content, bytes):
2330-
index_content = content.decode('utf-8', errors='ignore')
2331-
mime_type = self.get(itemid, 'type', self.default_mime_type)
2332-
self.db.indexer.add_text((self.classname, itemid, 'content'),
2333-
index_content, mime_type)
2334-
propvalues['content'] = content
2335-
2336-
# fire reactors
2337-
self.fireReactors('set', itemid, oldvalues)
2338-
return propvalues
2339-
2340-
def index(self, nodeid):
2341-
""" Add (or refresh) the node to search indexes.
2342-
2343-
Use the content-type property for the content property.
2344-
"""
2345-
# find all the String properties that have indexme
2346-
for prop, propclass in self.getprops().items():
2347-
if prop == 'content' and propclass.indexme:
2348-
mime_type = self.get(nodeid, 'type', self.default_mime_type)
2349-
index_content = self.get(nodeid, 'binary_content')
2350-
if bytes != str and isinstance(index_content, bytes):
2351-
index_content = index_content.decode('utf-8',
2352-
errors='ignore')
2353-
self.db.indexer.add_text((self.classname, nodeid, 'content'),
2354-
index_content, mime_type)
2355-
elif isinstance(propclass, hyperdb.String) and propclass.indexme:
2356-
# index them under (classname, nodeid, property)
2357-
try:
2358-
value = str(self.get(nodeid, prop))
2359-
except IndexError:
2360-
# node has been destroyed
2361-
continue
2362-
self.db.indexer.add_text((self.classname, nodeid, prop), value)
2363-
2364-
2365-
# deviation from spec - was called ItemClass
23662229
class IssueClass(Class, roundupdb.IssueClass):
2367-
# Overridden methods:
2230+
# Use for explicit upcalls in generic code, for py2 compat we cannot
2231+
# use super() without making everything a new-style class.
2232+
subclass = Class
23682233
def __init__(self, db, classname, **properties):
2369-
"""The newly-created class automatically includes the "messages",
2370-
"files", "nosy", and "superseder" properties. If the 'properties'
2371-
dictionary attempts to specify any of these properties or a
2372-
"creation" or "activity" property, a ValueError is raised.
2373-
"""
2374-
if 'title' not in properties:
2375-
properties['title'] = hyperdb.String(indexme='yes')
2376-
if 'messages' not in properties:
2377-
properties['messages'] = hyperdb.Multilink("msg")
2378-
if 'files' not in properties:
2379-
properties['files'] = hyperdb.Multilink("file")
2380-
if 'nosy' not in properties:
2381-
# note: journalling is turned off as it really just wastes
2382-
# space. this behaviour may be overridden in an instance
2383-
properties['nosy'] = hyperdb.Multilink("user", do_journal="no")
2384-
if 'superseder' not in properties:
2385-
properties['superseder'] = hyperdb.Multilink(classname)
2234+
self._update_properties(classname, properties)
23862235
Class.__init__(self, db, classname, **properties)
23872236

23882237
# vim: set et sts=4 sw=4 :

roundup/backends/rdbms_common.py

Lines changed: 9 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,9 @@
5959
import re
6060
import time
6161

62-
from hashlib import md5
63-
6462
# roundup modules
6563
from roundup import hyperdb, date, password, roundupdb, security, support
66-
from roundup.anypy.strings import b2s, bs2b, us2s, repr_export, eval_import
64+
from roundup.anypy.strings import us2s, repr_export, eval_import
6765
from roundup.backends.blobfiles import FileStorage
6866
from roundup.backends.indexer_common import get_indexer
6967
from roundup.backends.indexer_common import Indexer as CommonIndexer
@@ -3411,170 +3409,19 @@ def export_journals(self):
34113409

34123410

34133411
class FileClass(hyperdb.FileClass, Class):
3414-
"""This class defines a large chunk of data. To support this, it has a
3415-
mandatory String property "content" which is typically saved off
3416-
externally to the hyperdb.
3417-
3418-
The default MIME type of this data is defined by the
3419-
"default_mime_type" class attribute, which may be overridden by each
3420-
node if the class defines a "type" String property.
3421-
"""
3412+
# Use for explicit upcalls in generic code, for py2 compat we cannot
3413+
# use super() without making everything a new-style class.
3414+
subclass = Class
34223415
def __init__(self, db, classname, **properties):
3423-
"""The newly-created class automatically includes the "content"
3424-
and "type" properties.
3425-
"""
3426-
if 'content' not in properties:
3427-
properties['content'] = hyperdb.String(indexme='yes')
3428-
if 'type' not in properties:
3429-
properties['type'] = hyperdb.String()
3416+
self._update_properties(properties)
34303417
Class.__init__(self, db, classname, **properties)
34313418

3432-
def create(self, **propvalues):
3433-
""" snaffle the file propvalue and store in a file
3434-
"""
3435-
# we need to fire the auditors now, or the content property won't
3436-
# be in propvalues for the auditors to play with
3437-
self.fireAuditors('create', None, propvalues)
3438-
3439-
# now remove the content property so it's not stored in the db
3440-
content = propvalues['content']
3441-
del propvalues['content']
3442-
3443-
# do the database create
3444-
newid = self.create_inner(**propvalues)
3445-
3446-
# figure the mime type
3447-
mime_type = propvalues.get('type', self.default_mime_type)
3448-
3449-
# and index!
3450-
if self.properties['content'].indexme:
3451-
index_content = content
3452-
if bytes != str and isinstance(content, bytes):
3453-
index_content = content.decode('utf-8', errors='ignore')
3454-
self.db.indexer.add_text((self.classname, newid, 'content'),
3455-
index_content, mime_type)
3456-
3457-
# store off the content as a file
3458-
self.db.storefile(self.classname, newid, None, bs2b(content))
3459-
3460-
# fire reactors
3461-
self.fireReactors('create', newid, None)
3462-
3463-
return newid
3464-
3465-
def get(self, nodeid, propname, default=_marker, cache=1):
3466-
""" Trap the content propname and get it from the file
3467-
3468-
'cache' exists for backwards compatibility, and is not used.
3469-
"""
3470-
poss_msg = 'Possibly a access right configuration problem.'
3471-
if propname == 'content':
3472-
try:
3473-
return b2s(self.db.getfile(self.classname, nodeid, None))
3474-
except IOError as strerror:
3475-
# BUG: by catching this we donot see an error in the log.
3476-
return 'ERROR reading file: %s%s\n%s\n%s' % (
3477-
self.classname, nodeid, poss_msg, strerror)
3478-
except UnicodeDecodeError:
3479-
# if content is not text (e.g. jpeg file) we get
3480-
# unicode error trying to convert to string in python 3.
3481-
# trap it and supply an error message. Include md5sum
3482-
# of content as this string is included in the etag
3483-
# calculation of the object.
3484-
return ('%s%s is not text, retrieve using '
3485-
'binary_content property. mdsum: %s') % (
3486-
self.classname, nodeid,
3487-
md5(self.db.getfile(
3488-
self.classname,
3489-
nodeid,
3490-
None)).hexdigest()) # nosec - bandit md5 use ok
3491-
elif propname == 'binary_content':
3492-
return self.db.getfile(self.classname, nodeid, None)
3493-
3494-
if default is not _marker:
3495-
return Class.get(self, nodeid, propname, default)
3496-
else:
3497-
return Class.get(self, nodeid, propname)
3498-
3499-
def set(self, itemid, **propvalues):
3500-
""" Snarf the "content" propvalue and update it in a file
3501-
"""
3502-
self.fireAuditors('set', itemid, propvalues)
3503-
oldvalues = copy.deepcopy(self.db.getnode(self.classname, itemid))
3504-
3505-
# now remove the content property so it's not stored in the db
3506-
content = None
3507-
if 'content' in propvalues:
3508-
content = propvalues['content']
3509-
del propvalues['content']
3510-
3511-
# do the database create
3512-
propvalues = self.set_inner(itemid, **propvalues)
3513-
3514-
# do content?
3515-
if content:
3516-
# store and possibly index
3517-
self.db.storefile(self.classname, itemid, None, bs2b(content))
3518-
if self.properties['content'].indexme:
3519-
mime_type = self.get(itemid, 'type', self.default_mime_type)
3520-
index_content = content
3521-
if bytes != str and isinstance(content, bytes):
3522-
index_content = content.decode('utf-8', errors='ignore')
3523-
self.db.indexer.add_text((self.classname, itemid, 'content'),
3524-
index_content, mime_type)
3525-
propvalues['content'] = content
3526-
3527-
# fire reactors
3528-
self.fireReactors('set', itemid, oldvalues)
3529-
return propvalues
3530-
3531-
def index(self, nodeid):
3532-
""" Add (or refresh) the node to search indexes.
3533-
3534-
Use the content-type property for the content property.
3535-
"""
3536-
# find all the String properties that have indexme
3537-
for prop, propclass in self.getprops().items():
3538-
if prop == 'content' and propclass.indexme:
3539-
mime_type = self.get(nodeid, 'type', self.default_mime_type)
3540-
index_content = self.get(nodeid, 'binary_content')
3541-
if bytes != str and isinstance(index_content, bytes):
3542-
index_content = index_content.decode('utf-8',
3543-
errors='ignore')
3544-
self.db.indexer.add_text((self.classname, nodeid, 'content'),
3545-
index_content, mime_type)
3546-
elif isinstance(propclass, hyperdb.String) and propclass.indexme:
3547-
# index them under (classname, nodeid, property)
3548-
try:
3549-
value = str(self.get(nodeid, prop))
3550-
except IndexError:
3551-
# node has been destroyed
3552-
continue
3553-
self.db.indexer.add_text((self.classname, nodeid, prop), value)
3554-
3555-
3556-
# XXX deviation from spec - was called ItemClass
35573419
class IssueClass(Class, roundupdb.IssueClass):
3558-
# Overridden methods:
3420+
# Use for explicit upcalls in generic code, for py2 compat we cannot
3421+
# use super() without making everything a new-style class.
3422+
subclass = Class
35593423
def __init__(self, db, classname, **properties):
3560-
"""The newly-created class automatically includes the "messages",
3561-
"files", "nosy", and "superseder" properties. If the 'properties'
3562-
dictionary attempts to specify any of these properties or a
3563-
"creation", "creator", "activity" or "actor" property, a ValueError
3564-
is raised.
3565-
"""
3566-
if 'title' not in properties:
3567-
properties['title'] = hyperdb.String(indexme='yes')
3568-
if 'messages' not in properties:
3569-
properties['messages'] = hyperdb.Multilink("msg")
3570-
if 'files' not in properties:
3571-
properties['files'] = hyperdb.Multilink("file")
3572-
if 'nosy' not in properties:
3573-
# note: journalling is turned off as it really just wastes
3574-
# space. this behaviour may be overridden in an instance
3575-
properties['nosy'] = hyperdb.Multilink("user", do_journal="no")
3576-
if 'superseder' not in properties:
3577-
properties['superseder'] = hyperdb.Multilink(classname)
3424+
self._update_properties(classname, properties)
35783425
Class.__init__(self, db, classname, **properties)
35793426

35803427
# vim: set et sts=4 sw=4 :

0 commit comments

Comments
 (0)