Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
C
caosdb-pylib
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
caosdb
Software
caosdb-pylib
Commits
1a294d69
Commit
1a294d69
authored
Jul 9, 2021
by
Henrik tom Wörden
Committed by
Florian Spreckelsen
Jul 9, 2021
Browse files
Options
Downloads
Patches
Plain Diff
FIX: Treat dependencies inside containers when split for deletion
parent
a9456cad
No related branches found
No related tags found
2 merge requests
!33
MAINT: change arguments of create_user
,
!14
F delete container
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
src/caosdb/common/datatype.py
+2
-0
2 additions, 0 deletions
src/caosdb/common/datatype.py
src/caosdb/common/models.py
+130
-16
130 additions, 16 deletions
src/caosdb/common/models.py
unittests/test_container.py
+74
-9
74 additions, 9 deletions
unittests/test_container.py
with
206 additions
and
25 deletions
src/caosdb/common/datatype.py
+
2
−
0
View file @
1a294d69
...
@@ -45,6 +45,8 @@ def LIST(datatype):
...
@@ -45,6 +45,8 @@ def LIST(datatype):
def
get_list_datatype
(
datatype
):
def
get_list_datatype
(
datatype
):
"""
returns the datatype of the elements in the list
"""
"""
returns the datatype of the elements in the list
"""
if
not
isinstance
(
datatype
,
str
):
return
None
match
=
re
.
match
(
"
LIST(<|<)(?P<datatype>.*)(>|>)
"
,
datatype
)
match
=
re
.
match
(
"
LIST(<|<)(?P<datatype>.*)(>|>)
"
,
datatype
)
if
match
is
not
None
:
if
match
is
not
None
:
...
...
This diff is collapsed.
Click to expand it.
src/caosdb/common/models.py
+
130
−
16
View file @
1a294d69
...
@@ -40,26 +40,23 @@ from sys import hexversion
...
@@ -40,26 +40,23 @@ from sys import hexversion
from
tempfile
import
NamedTemporaryFile
from
tempfile
import
NamedTemporaryFile
from
warnings
import
warn
from
warnings
import
warn
from
caosdb.common.datatype
import
(
BOOLEAN
,
DATETIME
,
DOUBLE
,
INTEGER
,
TEXT
)
from
caosdb.common.datatype
import
(
BOOLEAN
,
DATETIME
,
DOUBLE
,
INTEGER
,
TEXT
,
from
caosdb.common.versioning
import
Version
is_list_datatype
,
is_reference
)
from
caosdb.common.state
import
State
from
caosdb.common.state
import
State
from
caosdb.common.utils
import
uuid
,
xml2str
from
caosdb.common.utils
import
uuid
,
xml2str
from
caosdb.common.versioning
import
Version
from
caosdb.configuration
import
get_config
from
caosdb.configuration
import
get_config
from
caosdb.connection.connection
import
get_connection
from
caosdb.connection.connection
import
get_connection
from
caosdb.connection.encode
import
MultipartParam
,
multipart_encode
from
caosdb.connection.encode
import
MultipartParam
,
multipart_encode
from
caosdb.exceptions
import
(
AmbiguousEntityError
,
from
caosdb.exceptions
import
(
AmbiguousEntityError
,
AuthorizationError
,
AuthorizationError
,
CaosDBConnectionError
,
CaosDBException
,
CaosDBException
,
CaosDBConnectionError
,
ConsistencyError
,
EmptyUniqueQueryError
,
ConsistencyError
,
EmptyUniqueQueryError
,
EntityDoesNotExistError
,
EntityError
,
EntityDoesNotExistError
,
EntityError
,
EntityHasNoDatatypeError
,
EntityHasNoDatatypeError
,
HTTPURITooLongError
,
MismatchingEntitiesError
,
MismatchingEntitiesError
,
QueryNotUniqueError
,
QueryNotUniqueError
,
TransactionError
,
TransactionError
,
UniqueNamesError
,
UniqueNamesError
,
UnqualifiedParentsError
,
UnqualifiedParentsError
,
UnqualifiedPropertiesError
,
UnqualifiedPropertiesError
)
HTTPURITooLongError
)
from
lxml
import
etree
from
lxml
import
etree
_ENTITY_URI_SEGMENT
=
"
Entity
"
_ENTITY_URI_SEGMENT
=
"
Entity
"
...
@@ -580,6 +577,7 @@ class Entity(object):
...
@@ -580,6 +577,7 @@ class Entity(object):
entity, or name.
entity, or name.
"""
"""
if
isinstance
(
key
,
int
):
if
isinstance
(
key
,
int
):
for
p
in
self
.
parents
:
for
p
in
self
.
parents
:
if
p
.
id
is
not
None
and
int
(
p
.
id
)
==
int
(
key
):
if
p
.
id
is
not
None
and
int
(
p
.
id
)
==
int
(
key
):
...
@@ -588,14 +586,17 @@ class Entity(object):
...
@@ -588,14 +586,17 @@ class Entity(object):
if
key
.
id
is
not
None
:
if
key
.
id
is
not
None
:
# first try by id
# first try by id
found
=
self
.
get_parent
(
int
(
key
.
id
))
found
=
self
.
get_parent
(
int
(
key
.
id
))
if
found
is
not
None
:
if
found
is
not
None
:
return
found
return
found
# otherwise by name
# otherwise by name
return
self
.
get_parent
(
key
.
name
)
return
self
.
get_parent
(
key
.
name
)
else
:
else
:
for
p
in
self
.
parents
:
for
p
in
self
.
parents
:
if
(
p
.
name
is
not
None
if
(
p
.
name
is
not
None
and
str
(
p
.
name
).
lower
()
==
str
(
key
).
lower
()):
and
str
(
p
.
name
).
lower
()
==
str
(
key
).
lower
()):
return
p
return
p
return
None
return
None
...
@@ -680,6 +681,7 @@ class Entity(object):
...
@@ -680,6 +681,7 @@ class Entity(object):
special_selector
=
None
special_selector
=
None
# iterating through the entity tree according to the selector
# iterating through the entity tree according to the selector
for
subselector
in
selector
:
for
subselector
in
selector
:
# selector does not match the structure, we cannot get a
# selector does not match the structure, we cannot get a
# property of non-entity
# property of non-entity
...
@@ -691,11 +693,13 @@ class Entity(object):
...
@@ -691,11 +693,13 @@ class Entity(object):
# selector does not match the structure, we did not get a
# selector does not match the structure, we did not get a
# property
# property
if
prop
is
None
:
if
prop
is
None
:
return
None
return
None
# if the property is a reference, we are interested in the
# if the property is a reference, we are interested in the
# corresponding entities attributes
# corresponding entities attributes
if
isinstance
(
prop
.
value
,
Entity
):
if
isinstance
(
prop
.
value
,
Entity
):
ref
=
prop
.
value
ref
=
prop
.
value
...
@@ -704,6 +708,7 @@ class Entity(object):
...
@@ -704,6 +708,7 @@ class Entity(object):
ref
=
prop
ref
=
prop
# if we saved a special selector before, apply it
# if we saved a special selector before, apply it
if
special_selector
is
None
:
if
special_selector
is
None
:
return
prop
.
value
return
prop
.
value
else
:
else
:
...
@@ -835,6 +840,7 @@ class Entity(object):
...
@@ -835,6 +840,7 @@ class Entity(object):
assert
isinstance
(
xml
,
etree
.
_Element
)
assert
isinstance
(
xml
,
etree
.
_Element
)
# unwrap wrapped entity
# unwrap wrapped entity
if
self
.
_wrapped_entity
is
not
None
:
if
self
.
_wrapped_entity
is
not
None
:
xml
=
self
.
_wrapped_entity
.
to_xml
(
xml
,
add_properties
)
xml
=
self
.
_wrapped_entity
.
to_xml
(
xml
,
add_properties
)
...
@@ -1074,6 +1080,7 @@ class Entity(object):
...
@@ -1074,6 +1080,7 @@ class Entity(object):
if
len
(
c
)
==
1
:
if
len
(
c
)
==
1
:
c
[
0
].
messages
.
extend
(
c
.
messages
)
c
[
0
].
messages
.
extend
(
c
.
messages
)
return
c
[
0
]
return
c
[
0
]
raise
QueryNotUniqueError
(
"
This retrieval was not unique!!!
"
)
raise
QueryNotUniqueError
(
"
This retrieval was not unique!!!
"
)
...
@@ -2145,6 +2152,7 @@ class _Messages(dict):
...
@@ -2145,6 +2152,7 @@ class _Messages(dict):
else
:
else
:
raise
TypeError
(
raise
TypeError
(
"
(
'
description
'
,
'
body
'
), (
'
body
'
), or
'
body
'
expected.
"
)
"
(
'
description
'
,
'
body
'
), (
'
body
'
), or
'
body
'
expected.
"
)
if
isinstance
(
value
,
Message
):
if
isinstance
(
value
,
Message
):
body
=
value
.
body
body
=
value
.
body
description
=
value
.
description
description
=
value
.
description
...
@@ -2314,6 +2322,7 @@ def _deletion_sync(e_local, e_remote):
...
@@ -2314,6 +2322,7 @@ def _deletion_sync(e_local, e_remote):
except
KeyError
:
except
KeyError
:
# deletion info wasn't there
# deletion info wasn't there
e_local
.
messages
=
e_remote
.
messages
e_local
.
messages
=
e_remote
.
messages
return
return
_basic_sync
(
e_local
,
e_remote
)
_basic_sync
(
e_local
,
e_remote
)
...
@@ -2506,6 +2515,7 @@ class Container(list):
...
@@ -2506,6 +2515,7 @@ class Container(list):
tmpid
=
0
tmpid
=
0
# users might already have specified some tmpids. -> look for smallest.
# users might already have specified some tmpids. -> look for smallest.
for
e
in
self
:
for
e
in
self
:
tmpid
=
min
(
tmpid
,
Container
.
_get_smallest_tmpid
(
e
))
tmpid
=
min
(
tmpid
,
Container
.
_get_smallest_tmpid
(
e
))
tmpid
-=
1
tmpid
-=
1
...
@@ -2725,6 +2735,7 @@ class Container(list):
...
@@ -2725,6 +2735,7 @@ class Container(list):
used_remote_entities
=
[]
used_remote_entities
=
[]
# match by cuid
# match by cuid
for
local_entity
in
self
:
for
local_entity
in
self
:
sync_dict
[
local_entity
]
=
None
sync_dict
[
local_entity
]
=
None
...
@@ -2753,6 +2764,7 @@ class Container(list):
...
@@ -2753,6 +2764,7 @@ class Container(list):
raise
MismatchingEntitiesError
(
msg
)
raise
MismatchingEntitiesError
(
msg
)
# match by id
# match by id
for
local_entity
in
self
:
for
local_entity
in
self
:
if
sync_dict
[
local_entity
]
is
None
and
local_entity
.
id
is
not
None
:
if
sync_dict
[
local_entity
]
is
None
and
local_entity
.
id
is
not
None
:
sync_remote_entities
=
[]
sync_remote_entities
=
[]
...
@@ -2777,6 +2789,7 @@ class Container(list):
...
@@ -2777,6 +2789,7 @@ class Container(list):
raise
MismatchingEntitiesError
(
msg
)
raise
MismatchingEntitiesError
(
msg
)
# match by path
# match by path
for
local_entity
in
self
:
for
local_entity
in
self
:
if
(
sync_dict
[
local_entity
]
is
None
if
(
sync_dict
[
local_entity
]
is
None
and
local_entity
.
path
is
not
None
):
and
local_entity
.
path
is
not
None
):
...
@@ -2806,6 +2819,7 @@ class Container(list):
...
@@ -2806,6 +2819,7 @@ class Container(list):
raise
MismatchingEntitiesError
(
msg
)
raise
MismatchingEntitiesError
(
msg
)
# match by name
# match by name
for
local_entity
in
self
:
for
local_entity
in
self
:
if
(
sync_dict
[
local_entity
]
is
None
if
(
sync_dict
[
local_entity
]
is
None
and
local_entity
.
name
is
not
None
):
and
local_entity
.
name
is
not
None
):
...
@@ -2855,7 +2869,60 @@ class Container(list):
...
@@ -2855,7 +2869,60 @@ class Container(list):
return
sync_dict
return
sync_dict
def
delete
(
self
,
raise_exception_on_error
=
True
,
flags
=
None
):
def
_test_dependencies_in_container
(
self
,
container
):
"""
This function returns those elements of a given container that are a dependency of another element of the same container.
Args:
container (Container): a caosdb container
Returns:
[set]: a set of unique elements that are a dependency of another element of `container`
"""
item_id
=
set
()
is_parent
=
set
()
is_property
=
set
()
is_being_referenced
=
set
()
dependent_parents
=
set
()
dependent_properties
=
set
()
dependent_references
=
set
()
dependencies
=
set
()
for
container_item
in
container
:
item_id
.
add
(
container_item
.
id
)
for
parents
in
container_item
.
get_parents
():
is_parent
.
add
(
parents
.
id
)
for
references
in
container_item
.
get_properties
():
if
is_reference
(
references
.
datatype
):
# add only if it is a reference, not a property
if
isinstance
(
references
.
value
,
int
):
is_being_referenced
.
add
(
references
.
value
)
elif
is_list_datatype
(
references
.
datatype
):
for
list_item
in
references
.
value
:
if
isinstance
(
list_item
,
int
):
is_being_referenced
.
add
(
list_item
)
else
:
is_being_referenced
.
add
(
list_item
.
id
)
else
:
try
:
is_being_referenced
.
add
(
references
.
value
.
id
)
except
:
pass
if
hasattr
(
references
,
'
id
'
):
is_property
.
add
(
references
.
id
)
dependent_parents
=
item_id
.
intersection
(
is_parent
)
dependent_properties
=
item_id
.
intersection
(
is_property
)
dependent_references
=
item_id
.
intersection
(
is_being_referenced
)
dependencies
=
dependent_parents
.
union
(
dependent_references
)
dependencies
=
dependencies
.
union
(
dependent_properties
)
return
dependencies
def
delete
(
self
,
raise_exception_on_error
=
True
,
flags
=
None
,
chunk_size
=
100
):
"""
Delete all entities in this container.
"""
Delete all entities in this container.
Entities are identified via their id if present and via their
Entities are identified via their id if present and via their
...
@@ -2866,16 +2933,43 @@ class Container(list):
...
@@ -2866,16 +2933,43 @@ class Container(list):
this happens, none of them will be deleted. It occurs an error
this happens, none of them will be deleted. It occurs an error
instead.
instead.
"""
"""
chunk_size
=
100
item_count
=
len
(
self
)
item_count
=
len
(
self
)
# Split Container in 'chunk_size'-sized containers (if necessary) to avoid error 414 Request-URI Too Long
# Split Container in 'chunk_size'-sized containers (if necessary) to avoid error 414 Request-URI Too Long
if
item_count
>
chunk_size
:
if
item_count
>
chunk_size
:
dependencies
=
self
.
_test_dependencies_in_container
(
self
)
'''
If there are as many dependencies as entities in the container and it is larger than chunk_size it cannot be split and deleted.
This case cannot be handled at the moment.
'''
if
len
(
dependencies
)
==
item_count
:
if
raise_exception_on_error
:
te
=
TransactionError
(
msg
=
"
The container is too large and with too many dependencies within to be deleted.
"
,
container
=
self
)
raise
te
return
self
dependencies_delete
=
Container
()
# items which have to be deleted later because of dependencies.
for
i
in
range
(
0
,
int
(
item_count
/
chunk_size
)
+
1
):
for
i
in
range
(
0
,
int
(
item_count
/
chunk_size
)
+
1
):
chunk
=
Container
()
chunk
=
Container
()
for
j
in
range
(
i
*
chunk_size
,
min
(
item_count
,
(
i
+
1
)
*
chunk_size
)):
for
j
in
range
(
i
*
chunk_size
,
min
(
item_count
,
(
i
+
1
)
*
chunk_size
)):
if
len
(
dependencies
):
if
self
[
j
].
id
in
dependencies
:
dependencies_delete
.
append
(
self
[
j
])
else
:
chunk
.
append
(
self
[
j
])
else
:
chunk
.
append
(
self
[
j
])
chunk
.
append
(
self
[
j
])
if
len
(
chunk
):
if
len
(
chunk
):
chunk
.
delete
()
chunk
.
delete
()
dependencies_delete
.
delete
()
return
self
return
self
if
len
(
self
)
==
0
:
if
len
(
self
)
==
0
:
...
@@ -3513,6 +3607,7 @@ class ACL():
...
@@ -3513,6 +3607,7 @@ class ACL():
result
.
_priority_grants
.
update
(
self
.
_priority_grants
)
result
.
_priority_grants
.
update
(
self
.
_priority_grants
)
result
.
_priority_denials
.
update
(
other
.
_priority_denials
)
result
.
_priority_denials
.
update
(
other
.
_priority_denials
)
result
.
_priority_denials
.
update
(
self
.
_priority_denials
)
result
.
_priority_denials
.
update
(
self
.
_priority_denials
)
return
result
return
result
def
__eq__
(
self
,
other
):
def
__eq__
(
self
,
other
):
...
@@ -3803,6 +3898,7 @@ class Query():
...
@@ -3803,6 +3898,7 @@ class Query():
connection
=
get_connection
()
connection
=
get_connection
()
flags
=
self
.
flags
flags
=
self
.
flags
if
cache
is
False
:
if
cache
is
False
:
flags
[
"
cache
"
]
=
"
false
"
flags
[
"
cache
"
]
=
"
false
"
query_dict
=
dict
(
flags
)
query_dict
=
dict
(
flags
)
...
@@ -3829,9 +3925,11 @@ class Query():
...
@@ -3829,9 +3925,11 @@ class Query():
if
len
(
cresp
)
>
1
and
raise_exception_on_error
:
if
len
(
cresp
)
>
1
and
raise_exception_on_error
:
raise
QueryNotUniqueError
(
raise
QueryNotUniqueError
(
"
Query
'
{}
'
wasn
'
t unique.
"
.
format
(
self
.
q
))
"
Query
'
{}
'
wasn
'
t unique.
"
.
format
(
self
.
q
))
if
len
(
cresp
)
==
0
and
raise_exception_on_error
:
if
len
(
cresp
)
==
0
and
raise_exception_on_error
:
raise
EmptyUniqueQueryError
(
raise
EmptyUniqueQueryError
(
"
Query
'
{}
'
found no results.
"
.
format
(
self
.
q
))
"
Query
'
{}
'
found no results.
"
.
format
(
self
.
q
))
if
len
(
cresp
)
==
1
:
if
len
(
cresp
)
==
1
:
r
=
cresp
[
0
]
r
=
cresp
[
0
]
r
.
messages
.
extend
(
cresp
.
messages
)
r
.
messages
.
extend
(
cresp
.
messages
)
...
@@ -3938,6 +4036,7 @@ class Info():
...
@@ -3938,6 +4036,7 @@ class Info():
for
e
in
xml
:
for
e
in
xml
:
m
=
_parse_single_xml_element
(
e
)
m
=
_parse_single_xml_element
(
e
)
if
isinstance
(
m
,
UserInfo
):
if
isinstance
(
m
,
UserInfo
):
self
.
user_info
=
m
self
.
user_info
=
m
else
:
else
:
...
@@ -4097,13 +4196,16 @@ def _evaluate_and_add_error(parent_error, ent):
...
@@ -4097,13 +4196,16 @@ def _evaluate_and_add_error(parent_error, ent):
Parent error with new exception(s) attached to it.
Parent error with new exception(s) attached to it.
"""
"""
if
isinstance
(
ent
,
(
Entity
,
QueryTemplate
)):
if
isinstance
(
ent
,
(
Entity
,
QueryTemplate
)):
# Check all error messages
# Check all error messages
found114
=
False
found114
=
False
found116
=
False
found116
=
False
for
err
in
ent
.
get_errors
():
for
err
in
ent
.
get_errors
():
# Evaluate specific EntityErrors depending on the error
# Evaluate specific EntityErrors depending on the error
# code
# code
if
err
.
code
is
not
None
:
if
err
.
code
is
not
None
:
if
int
(
err
.
code
)
==
101
:
# ent doesn't exist
if
int
(
err
.
code
)
==
101
:
# ent doesn't exist
new_exc
=
EntityDoesNotExistError
(
entity
=
ent
,
new_exc
=
EntityDoesNotExistError
(
entity
=
ent
,
...
@@ -4120,6 +4222,7 @@ def _evaluate_and_add_error(parent_error, ent):
...
@@ -4120,6 +4222,7 @@ def _evaluate_and_add_error(parent_error, ent):
found114
=
True
found114
=
True
new_exc
=
UnqualifiedPropertiesError
(
entity
=
ent
,
new_exc
=
UnqualifiedPropertiesError
(
entity
=
ent
,
error
=
err
)
error
=
err
)
for
prop
in
ent
.
get_properties
():
for
prop
in
ent
.
get_properties
():
new_exc
=
_evaluate_and_add_error
(
new_exc
,
new_exc
=
_evaluate_and_add_error
(
new_exc
,
prop
)
prop
)
...
@@ -4127,6 +4230,7 @@ def _evaluate_and_add_error(parent_error, ent):
...
@@ -4127,6 +4230,7 @@ def _evaluate_and_add_error(parent_error, ent):
found116
=
True
found116
=
True
new_exc
=
UnqualifiedParentsError
(
entity
=
ent
,
new_exc
=
UnqualifiedParentsError
(
entity
=
ent
,
error
=
err
)
error
=
err
)
for
par
in
ent
.
get_parents
():
for
par
in
ent
.
get_parents
():
new_exc
=
_evaluate_and_add_error
(
new_exc
,
new_exc
=
_evaluate_and_add_error
(
new_exc
,
par
)
par
)
...
@@ -4137,21 +4241,28 @@ def _evaluate_and_add_error(parent_error, ent):
...
@@ -4137,21 +4241,28 @@ def _evaluate_and_add_error(parent_error, ent):
parent_error
.
add_error
(
new_exc
)
parent_error
.
add_error
(
new_exc
)
# Check for possible errors in parents and properties that
# Check for possible errors in parents and properties that
# weren't detected up to here
# weren't detected up to here
if
not
found114
:
if
not
found114
:
dummy_err
=
EntityError
(
entity
=
ent
)
dummy_err
=
EntityError
(
entity
=
ent
)
for
prop
in
ent
.
get_properties
():
for
prop
in
ent
.
get_properties
():
dummy_err
=
_evaluate_and_add_error
(
dummy_err
,
prop
)
dummy_err
=
_evaluate_and_add_error
(
dummy_err
,
prop
)
if
dummy_err
.
errors
:
if
dummy_err
.
errors
:
parent_error
.
add_error
(
dummy_err
)
parent_error
.
add_error
(
dummy_err
)
if
not
found116
:
if
not
found116
:
dummy_err
=
EntityError
(
entity
=
ent
)
dummy_err
=
EntityError
(
entity
=
ent
)
for
par
in
ent
.
get_parents
():
for
par
in
ent
.
get_parents
():
dummy_err
=
_evaluate_and_add_error
(
dummy_err
,
par
)
dummy_err
=
_evaluate_and_add_error
(
dummy_err
,
par
)
if
dummy_err
.
errors
:
if
dummy_err
.
errors
:
parent_error
.
add_error
(
dummy_err
)
parent_error
.
add_error
(
dummy_err
)
elif
isinstance
(
ent
,
Container
):
elif
isinstance
(
ent
,
Container
):
parent_error
.
container
=
ent
parent_error
.
container
=
ent
if
ent
.
get_errors
()
is
not
None
:
if
ent
.
get_errors
()
is
not
None
:
parent_error
.
code
=
ent
.
get_errors
()[
0
].
code
parent_error
.
code
=
ent
.
get_errors
()[
0
].
code
# In the highly unusual case of more than one error
# In the highly unusual case of more than one error
...
@@ -4159,6 +4270,7 @@ def _evaluate_and_add_error(parent_error, ent):
...
@@ -4159,6 +4270,7 @@ def _evaluate_and_add_error(parent_error, ent):
parent_error
.
msg
=
'
\n
'
.
join
(
parent_error
.
msg
=
'
\n
'
.
join
(
[
x
.
description
for
x
in
ent
.
get_errors
()])
[
x
.
description
for
x
in
ent
.
get_errors
()])
# Go through all container elements and add them:
# Go through all container elements and add them:
for
elt
in
ent
:
for
elt
in
ent
:
parent_error
=
_evaluate_and_add_error
(
parent_error
,
elt
)
parent_error
=
_evaluate_and_add_error
(
parent_error
,
elt
)
...
@@ -4184,10 +4296,12 @@ def raise_errors(arg0):
...
@@ -4184,10 +4296,12 @@ def raise_errors(arg0):
transaction_error
=
_evaluate_and_add_error
(
TransactionError
(),
transaction_error
=
_evaluate_and_add_error
(
TransactionError
(),
arg0
)
arg0
)
# Raise if any error was found
# Raise if any error was found
if
len
(
transaction_error
.
all_errors
)
>
0
:
if
len
(
transaction_error
.
all_errors
)
>
0
:
raise
transaction_error
raise
transaction_error
# Cover the special case of an empty container with error
# Cover the special case of an empty container with error
# message(s) (e.g. query syntax error)
# message(s) (e.g. query syntax error)
if
(
transaction_error
.
container
is
not
None
and
if
(
transaction_error
.
container
is
not
None
and
transaction_error
.
container
.
has_errors
()):
transaction_error
.
container
.
has_errors
()):
raise
transaction_error
raise
transaction_error
...
...
This diff is collapsed.
Click to expand it.
unittests/test_container.py
+
74
−
9
View file @
1a294d69
...
@@ -25,28 +25,28 @@
...
@@ -25,28 +25,28 @@
"""
Tests for the Container class.
"""
"""
Tests for the Container class.
"""
from
__future__
import
absolute_import
from
__future__
import
absolute_import
import
caosdb
as
c
import
caosdb
as
db
def
test_get_property_values
():
def
test_get_property_values
():
rt_house
=
c
.
RecordType
(
"
House
"
)
rt_house
=
db
.
RecordType
(
"
House
"
)
rt_window
=
c
.
RecordType
(
"
Window
"
)
rt_window
=
db
.
RecordType
(
"
Window
"
)
rt_owner
=
c
.
RecordType
(
"
Owner
"
)
rt_owner
=
db
.
RecordType
(
"
Owner
"
)
p_height
=
c
.
Property
(
"
Height
"
,
datatype
=
c
.
DOUBLE
)
p_height
=
db
.
Property
(
"
Height
"
,
datatype
=
db
.
DOUBLE
)
window
=
c
.
Record
().
add_parent
(
rt_window
)
window
=
db
.
Record
().
add_parent
(
rt_window
)
window
.
id
=
1001
window
.
id
=
1001
window
.
add_property
(
p_height
,
20.5
,
unit
=
"
m
"
)
window
.
add_property
(
p_height
,
20.5
,
unit
=
"
m
"
)
owner
=
c
.
Record
(
"
The Queen
"
).
add_parent
(
rt_owner
)
owner
=
db
.
Record
(
"
The Queen
"
).
add_parent
(
rt_owner
)
house
=
c
.
Record
(
"
Buckingham Palace
"
)
house
=
db
.
Record
(
"
Buckingham Palace
"
)
house
.
add_parent
(
rt_house
)
house
.
add_parent
(
rt_house
)
house
.
add_property
(
rt_owner
,
owner
)
house
.
add_property
(
rt_owner
,
owner
)
house
.
add_property
(
rt_window
,
window
)
house
.
add_property
(
rt_window
,
window
)
house
.
add_property
(
p_height
,
40.2
,
unit
=
"
ft
"
)
house
.
add_property
(
p_height
,
40.2
,
unit
=
"
ft
"
)
container
=
c
.
Container
()
container
=
db
.
Container
()
container
.
extend
([
container
.
extend
([
house
,
house
,
owner
owner
...
@@ -77,3 +77,68 @@ def test_get_property_values():
...
@@ -77,3 +77,68 @@ def test_get_property_values():
assert
container
.
get_property_values
(
"
non-existing
"
)
==
[(
None
,),
(
None
,)]
assert
container
.
get_property_values
(
"
non-existing
"
)
==
[(
None
,),
(
None
,)]
assert
container
.
get_property_values
(
"
name
"
)
==
[(
house
.
name
,),
assert
container
.
get_property_values
(
"
name
"
)
==
[(
house
.
name
,),
(
owner
.
name
,)]
(
owner
.
name
,)]
def
test_container_dependencies_for_deletion
():
not_included_rt
=
1000
rt
=
db
.
RecordType
(
"
Just a RecordType
"
)
rt
.
id
=
1001
rt_record_with_parent
=
db
.
RecordType
(
"
Records with parent
"
)
rt_record_with_parent
.
id
=
1005
property_which_is_not_a_record
=
db
.
Property
(
"
Normal Property
"
,
datatype
=
db
.
DOUBLE
,
value
=
1006
)
property_which_is_not_a_record
.
id
=
1006
property_which_shall_be_deleted
=
db
.
Property
(
"
Normal Property 2
"
,
datatype
=
db
.
DOUBLE
,
value
=
1006
)
property_which_shall_be_deleted
.
id
=
1007
record_without_dependencies
=
db
.
Record
().
add_parent
(
not_included_rt
)
record_without_dependencies
.
id
=
2003
record_referenced
=
db
.
Record
().
add_parent
(
not_included_rt
)
record_referenced
.
id
=
2002
record_with_dependencies
=
db
.
Record
().
add_parent
(
not_included_rt
)
record_with_dependencies
.
id
=
2004
record_with_dependencies
.
add_property
(
not_included_rt
,
record_referenced
)
record_with_parent
=
db
.
Record
().
add_parent
(
rt_record_with_parent
)
record_with_parent
.
id
=
2005
record_with_property_which_is_not_a_record
=
db
.
Record
(
).
add_parent
(
not_included_rt
)
record_with_property_which_is_not_a_record
.
id
=
2006
record_with_property_which_is_not_a_record
.
add_property
(
property_which_is_not_a_record
)
record_with_property_which_is_not_a_record
.
add_property
(
property_which_shall_be_deleted
)
container
=
db
.
Container
()
container
.
extend
([
rt
,
rt_record_with_parent
,
# 1005, dependency
record_without_dependencies
,
property_which_shall_be_deleted
,
# 1007, dependency
record_referenced
,
# 2002, dependency
record_with_dependencies
,
record_with_parent
,
record_with_property_which_is_not_a_record
])
assert
(
db
.
Container
().
_test_dependencies_in_container
(
container
)
==
{
2002
,
1005
,
1007
})
def
test_container_dependencies_for_deletion_with_lists
():
not_included_rt
=
1000
record_referenced
=
db
.
Record
().
add_parent
(
not_included_rt
)
record_referenced
.
id
=
2001
record_with_list
=
db
.
Record
().
add_parent
(
not_included_rt
)
record_with_list
.
id
=
2002
record_with_list
.
add_property
(
not_included_rt
,
datatype
=
db
.
LIST
(
not_included_rt
),
value
=
[
record_referenced
,
2003
,
2004
,
2005
,
2006
])
container
=
db
.
Container
()
container
.
extend
([
record_with_list
,
record_referenced
])
assert
db
.
Container
().
_test_dependencies_in_container
(
container
)
==
{
2001
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment