Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
ParsePy
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
ParsePy
Commits
ffacfc4b
Commit
ffacfc4b
authored
Jul 19, 2014
by
David Robinson
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #48 from farin/master
Python3, tests for new features, small fix for __len__ and query copying
parents
cd9ef16a
25acbfb7
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
283 additions
and
295 deletions
+283
-295
README.mkd
+2
-3
parse_rest/connection.py
+15
-15
parse_rest/datatypes.py
+73
-99
parse_rest/installation.py
+3
-3
parse_rest/query.py
+41
-43
parse_rest/tests.py
+137
-124
parse_rest/user.py
+5
-5
setup.py
+7
-3
No files found.
README.mkd
View file @
ffacfc4b
...
...
@@ -52,10 +52,9 @@ in the app and may accidentally replace or change existing objects.
*
install the
[
Parse CloudCode tool
](
https://www.parse.com/docs/cloud_code_guide
)
You can then test the installation by running the following
in a Python prompt
:
You can then test the installation by running the following
command
:
from parse_rest import tests
tests.run_tests()
python -m 'parse_rest.tests'
Usage
...
...
parse_rest/connection.py
View file @
ffacfc4b
...
...
@@ -11,18 +11,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
try
:
from
urllib2
import
Request
,
urlopen
,
HTTPError
from
urllib
import
urlencode
except
ImportError
:
# is Python3
from
urllib.request
import
Request
,
urlopen
from
urllib.error
import
HTTPError
from
urllib.parse
import
urlencode
from
six.moves.urllib.request
import
Request
,
urlopen
from
six.moves.urllib.error
import
HTTPError
from
six.moves.urllib.parse
import
urlencode
import
json
import
core
from
parse_rest
import
core
API_ROOT
=
'https://api.parse.com/1'
ACCESS_KEYS
=
{}
...
...
@@ -60,8 +55,7 @@ class ParseBase(object):
command.
"""
if
batch
:
ret
=
{
"method"
:
http_verb
,
"path"
:
uri
.
split
(
"parse.com"
)[
1
]}
ret
=
{
"method"
:
http_verb
,
"path"
:
uri
.
split
(
"parse.com"
,
1
)[
1
]}
if
kw
:
ret
[
"body"
]
=
kw
return
ret
...
...
@@ -79,6 +73,8 @@ class ParseBase(object):
if
http_verb
==
'GET'
and
data
:
url
+=
'?
%
s'
%
urlencode
(
kw
)
data
=
None
else
:
data
=
data
.
encode
(
'utf-8'
)
request
=
Request
(
url
,
data
,
headers
)
request
.
add_header
(
'Content-type'
,
'application/json'
)
...
...
@@ -101,7 +97,7 @@ class ParseBase(object):
}
.
get
(
e
.
code
,
core
.
ParseError
)
raise
exc
(
e
.
read
())
return
json
.
loads
(
response
.
read
())
return
json
.
loads
(
response
.
read
()
.
decode
(
'utf-8'
)
)
@classmethod
def
GET
(
cls
,
uri
,
**
kw
):
...
...
@@ -129,7 +125,11 @@ class ParseBatcher(ParseBase):
Given a list of create, update or delete methods to call, call all
of them in a single batch operation.
"""
queries
,
callbacks
=
zip
(
*
[
m
(
batch
=
True
)
for
m
in
methods
])
methods
=
list
(
methods
)
# methods can be iterator
if
not
methods
:
#accepts also empty list (or generator) - it allows call batch directly with query result (eventually empty)
return
queries
,
callbacks
=
list
(
zip
(
*
[
m
(
batch
=
True
)
for
m
in
methods
]))
# perform all the operations in one batch
responses
=
self
.
execute
(
""
,
"POST"
,
requests
=
queries
)
# perform the callbacks with the response data (updating the existing
...
...
@@ -139,8 +139,8 @@ class ParseBatcher(ParseBase):
def
batch_save
(
self
,
objects
):
"""save a list of objects in one operation"""
self
.
batch
(
[
o
.
save
for
o
in
objects
]
)
self
.
batch
(
o
.
save
for
o
in
objects
)
def
batch_delete
(
self
,
objects
):
"""delete a list of objects in one operation"""
self
.
batch
(
[
o
.
delete
for
o
in
objects
]
)
self
.
batch
(
o
.
delete
for
o
in
objects
)
parse_rest/datatypes.py
View file @
ffacfc4b
...
...
@@ -10,18 +10,29 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from
__future__
import
unicode_literals
import
base64
import
datetime
import
six
from
connection
import
API_ROOT
,
ParseBase
from
query
import
QueryManager
from
parse_rest.connection
import
API_ROOT
,
ParseBase
from
parse_rest.query
import
QueryManager
def
complex_type
(
name
=
None
):
'''Decorator for registering complex types'''
def
wrapped
(
cls
):
ParseType
.
type_mapping
[
name
or
cls
.
__name__
]
=
cls
return
cls
return
wrapped
class
ParseType
(
object
):
type_mapping
=
{}
@staticmethod
def
convert_from_parse
(
parse_data
,
class_name
):
def
convert_from_parse
(
parse_data
):
is_parse_type
=
isinstance
(
parse_data
,
dict
)
and
'__type'
in
parse_data
...
...
@@ -29,28 +40,8 @@ class ParseType(object):
if
not
is_parse_type
:
return
parse_data
# determine just which kind of parse type this element is - ie: a built in parse type such as File, Pointer, User etc
parse_type
=
parse_data
[
'__type'
]
# if its a pointer, we need to handle to ensure that we don't mishandle a circular reference
if
parse_type
==
"Pointer"
:
# grab the pointer object here
return
Pointer
.
from_native
(
class_name
,
**
parse_data
)
# embedded object by select_related
if
parse_type
==
"Object"
:
return
EmbeddedObject
.
from_native
(
class_name
,
**
parse_data
)
# now handle the other parse types accordingly
native
=
{
'Date'
:
Date
,
'Bytes'
:
Binary
,
'GeoPoint'
:
GeoPoint
,
'File'
:
File
,
'Relation'
:
Relation
}
.
get
(
parse_type
)
return
native
and
native
.
from_native
(
**
parse_data
)
or
parse_data
native
=
ParseType
.
type_mapping
.
get
(
parse_data
[
'__type'
])
return
native
.
from_native
(
**
parse_data
)
if
native
else
parse_data
@staticmethod
def
convert_to_parse
(
python_object
,
as_pointer
=
False
):
...
...
@@ -67,7 +58,7 @@ class ParseType(object):
transformation_map
=
{
datetime
.
datetime
:
Date
,
Object
:
Pointer
}
}
if
python_type
in
transformation_map
:
klass
=
transformation_map
.
get
(
python_type
)
...
...
@@ -83,39 +74,20 @@ class ParseType(object):
return
cls
(
**
kw
)
def
_to_native
(
self
):
r
eturn
self
.
_value
r
aise
NotImplementedError
(
"_to_native must be overridden"
)
@complex_type
(
'Pointer'
)
class
Pointer
(
ParseType
):
@classmethod
def
_prevent_circular
(
cls
,
parent_class_name
,
objectData
):
# TODO this should be replaced with more clever checking, instead of simple class mathching original id should be compared
# also circular refs through more object are now ignored, in fact lazy loaded references will be best solution
objectData
=
dict
(
objectData
)
# now lets see if we have any references to the parent class here
for
key
,
value
in
objectData
.
iteritems
():
if
isinstance
(
value
,
dict
)
and
"className"
in
value
and
value
[
"className"
]
==
parent_class_name
:
# simply put the reference here as a string -- not sure what the drawbacks are for this but it works for me
objectData
[
key
]
=
value
[
"objectId"
]
return
objectData
@classmethod
def
from_native
(
cls
,
parent_class_name
=
None
,
**
kw
):
# grab the object data manually here so we can manipulate it before passing back an actual object
def
from_native
(
cls
,
**
kw
):
# create object with only objectId and unloaded flag. it is automatically loaded when any other field is accessed
klass
=
Object
.
factory
(
kw
.
get
(
'className'
))
objectData
=
klass
.
GET
(
"/"
+
kw
.
get
(
'objectId'
)
)
return
klass
(
objectId
=
kw
.
get
(
'objectId'
),
_is_loaded
=
False
)
# now lets check if we have circular references here
if
parent_class_name
:
objectData
=
cls
.
_prevent_circular
(
parent_class_name
,
objectData
)
# set a temporary flag that will remove the recursive pointer types etc
klass
=
Object
.
factory
(
kw
.
get
(
'className'
))
return
klass
(
**
objectData
)
def
__init__
(
self
,
obj
):
self
.
_object
=
obj
def
_to_native
(
self
):
...
...
@@ -123,24 +95,25 @@ class Pointer(ParseType):
'__type'
:
'Pointer'
,
'className'
:
self
.
_object
.
__class__
.
__name__
,
'objectId'
:
self
.
_object
.
objectId
}
}
@complex_type
(
'Object'
)
class
EmbeddedObject
(
ParseType
):
@classmethod
def
from_native
(
cls
,
parent_class_name
=
None
,
**
kw
):
if
parent_class_name
:
kw
=
Pointer
.
_prevent_circular
(
parent_class_name
,
kw
)
def
from_native
(
cls
,
**
kw
):
klass
=
Object
.
factory
(
kw
.
get
(
'className'
))
return
klass
(
**
kw
)
@complex_type
()
class
Relation
(
ParseType
):
@classmethod
def
from_native
(
cls
,
**
kw
):
pass
@complex_type
()
class
Date
(
ParseType
):
FORMAT
=
'
%
Y-
%
m-
%
dT
%
H:
%
M:
%
S.
%
f
%
Z'
...
...
@@ -157,7 +130,7 @@ class Date(ParseType):
"""Can be initialized either with a string or a datetime"""
if
isinstance
(
date
,
datetime
.
datetime
):
self
.
_date
=
date
elif
isinstance
(
date
,
unicode
):
elif
isinstance
(
date
,
six
.
string_types
):
self
.
_date
=
Date
.
_from_str
(
date
)
def
_to_native
(
self
):
...
...
@@ -166,6 +139,7 @@ class Date(ParseType):
}
@complex_type
(
'Bytes'
)
class
Binary
(
ParseType
):
@classmethod
...
...
@@ -180,6 +154,7 @@ class Binary(ParseType):
return
{
'__type'
:
'Bytes'
,
'base64'
:
self
.
_encoded
}
@complex_type
()
class
GeoPoint
(
ParseType
):
@classmethod
...
...
@@ -198,6 +173,7 @@ class GeoPoint(ParseType):
}
@complex_type
()
class
File
(
ParseType
):
@classmethod
...
...
@@ -232,14 +208,10 @@ class Function(ParseBase):
return
self
.
POST
(
'/'
+
self
.
name
,
**
kwargs
)
class
ParseResource
(
ParseBase
,
Pointer
):
class
ParseResource
(
ParseBase
):
PROTECTED_ATTRIBUTES
=
[
'objectId'
,
'createdAt'
,
'updatedAt'
]
@classmethod
def
retrieve
(
cls
,
resource_id
):
return
cls
(
**
cls
.
GET
(
'/'
+
resource_id
))
@property
def
_editable_attrs
(
self
):
protected_attrs
=
self
.
__class__
.
PROTECTED_ATTRIBUTES
...
...
@@ -247,20 +219,23 @@ class ParseResource(ParseBase, Pointer):
return
dict
([(
k
,
v
)
for
k
,
v
in
self
.
__dict__
.
items
()
if
allowed
(
k
)])
def
__init__
(
self
,
**
kw
):
self
.
objectId
=
None
self
.
_init_attrs
(
kw
)
def
__getattr__
(
self
,
attr
):
# if object is not loaded and attribute is missing, try to load it
if
not
self
.
__dict__
.
get
(
'_is_loaded'
,
True
):
del
self
.
_is_loaded
self
.
_init_attrs
(
self
.
GET
(
self
.
_absolute_url
))
return
object
.
__getattribute__
(
self
,
attr
)
#preserve default if attr not exists
for
key
,
value
in
kw
.
items
():
setattr
(
self
,
key
,
ParseType
.
convert_from_parse
(
value
,
self
.
__class__
.
__name__
))
def
_init_attrs
(
self
,
args
):
for
key
,
value
in
six
.
iteritems
(
args
):
setattr
(
self
,
key
,
ParseType
.
convert_from_parse
(
value
))
def
_to_native
(
self
):
return
ParseType
.
convert_to_parse
(
self
)
def
_get_object_id
(
self
):
return
self
.
__dict__
.
get
(
'_object_id'
)
def
_set_object_id
(
self
,
value
):
if
'_object_id'
in
self
.
__dict__
:
raise
ValueError
(
'Can not re-set object id'
)
self
.
_object_id
=
value
def
_get_updated_datetime
(
self
):
return
self
.
__dict__
.
get
(
'_updated_at'
)
and
self
.
_updated_at
.
_date
...
...
@@ -294,8 +269,7 @@ class ParseResource(ParseBase, Pointer):
call_back
(
response
)
def
_update
(
self
,
batch
=
False
):
response
=
self
.
__class__
.
PUT
(
self
.
_absolute_url
,
batch
=
batch
,
**
self
.
_to_native
())
response
=
self
.
__class__
.
PUT
(
self
.
_absolute_url
,
batch
=
batch
,
**
self
.
_to_native
())
def
call_back
(
response_dict
):
self
.
updatedAt
=
response_dict
[
'updatedAt'
]
...
...
@@ -307,46 +281,48 @@ class ParseResource(ParseBase, Pointer):
def
delete
(
self
,
batch
=
False
):
response
=
self
.
__class__
.
DELETE
(
self
.
_absolute_url
,
batch
=
batch
)
def
call_back
(
response_dict
):
self
.
__dict__
=
{}
if
batch
:
return
response
,
call_back
else
:
call_back
(
response
)
return
response
,
lambda
response_dict
:
None
_absolute_url
=
property
(
lambda
self
:
'/'
.
join
([
self
.
__class__
.
ENDPOINT_ROOT
,
self
.
objectId
])
)
_absolute_url
=
property
(
lambda
self
:
'/'
.
join
([
self
.
__class__
.
ENDPOINT_ROOT
,
self
.
objectId
]))
objectId
=
property
(
_get_object_id
,
_set_object_id
)
createdAt
=
property
(
_get_created_datetime
,
_set_created_datetime
)
updatedAt
=
property
(
_get_updated_datetime
,
_set_updated_datetime
)
def
__repr__
(
self
):
return
'<
%
s:
%
s>'
%
(
unicode
(
self
.
__class__
.
__name__
)
,
self
.
objectId
)
return
'<
%
s:
%
s>'
%
(
self
.
__class__
.
__name__
,
self
.
objectId
)
class
ObjectMetaclass
(
type
):
def
__new__
(
cls
,
name
,
bases
,
dct
):
cls
=
super
(
ObjectMetaclass
,
cls
)
.
__new__
(
cls
,
name
,
bases
,
dct
)
cls
.
set_endpoint_root
()
cls
.
Query
=
QueryManager
(
cls
)
def
__new__
(
mcs
,
name
,
bases
,
dct
):
cls
=
super
(
ObjectMetaclass
,
mcs
)
.
__new__
(
mcs
,
name
,
bases
,
dct
)
# attr check must be here because of specific six.with_metaclass implemetantion where metaclass is used also for
# internal NewBase which hasn't set_endpoint_root method
if
hasattr
(
cls
,
'set_endpoint_root'
):
cls
.
set_endpoint_root
()
cls
.
Query
=
QueryManager
(
cls
)
return
cls
class
Object
(
ParseResource
):
__metaclass__
=
ObjectMetaclass
class
Object
(
six
.
with_metaclass
(
ObjectMetaclass
,
ParseResource
)):
ENDPOINT_ROOT
=
'/'
.
join
([
API_ROOT
,
'classes'
])
@classmethod
def
factory
(
cls
,
class_name
):
class
DerivedClass
(
cls
):
pass
DerivedClass
.
__name__
=
str
(
class_name
)
DerivedClass
.
set_endpoint_root
()
return
DerivedClass
"""find proper Object subclass matching class_name
system types like _User are mapped to types without underscore (parse_resr.user.User)
If user don't declare matching type, class is created on the fly
"""
class_name
=
str
(
class_name
.
lstrip
(
'_'
))
types
=
ParseResource
.
__subclasses__
()
while
types
:
t
=
types
.
pop
()
if
t
.
__name__
==
class_name
:
return
t
types
.
extend
(
t
.
__subclasses__
())
else
:
return
type
(
class_name
,
(
Object
,),
{})
@classmethod
def
set_endpoint_root
(
cls
):
...
...
@@ -357,15 +333,13 @@ class Object(ParseResource):
@property
def
_absolute_url
(
self
):
if
not
self
.
objectId
:
return
None
if
not
self
.
objectId
:
return
None
return
'/'
.
join
([
self
.
__class__
.
ENDPOINT_ROOT
,
self
.
objectId
])
@property
def
as_pointer
(
self
):
return
Pointer
(
**
{
'className'
:
self
.
__class__
.
__name__
,
'objectId'
:
self
.
objectId
})
return
Pointer
(
self
)
def
increment
(
self
,
key
,
amount
=
1
):
"""
...
...
parse_rest/installation.py
View file @
ffacfc4b
...
...
@@ -11,9 +11,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from
connection
import
API_ROOT
from
datatypes
import
ParseResource
from
query
import
QueryManager
from
parse_rest.
connection
import
API_ROOT
from
parse_rest.
datatypes
import
ParseResource
from
parse_rest.
query
import
QueryManager
class
Installation
(
ParseResource
):
...
...
parse_rest/query.py
View file @
ffacfc4b
...
...
@@ -12,13 +12,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import
json
import
collections
import
copy
import
collections
try
:
unicode
=
unicode
except
NameError
:
unicode
=
str
class
QueryResourceDoesNotExist
(
Exception
):
'''Query returned no results'''
...
...
@@ -41,9 +37,8 @@ class QueryManager(object):
return
[
klass
(
**
it
)
for
it
in
klass
.
GET
(
uri
,
**
kw
)
.
get
(
'results'
)]
def
_count
(
self
,
**
kw
):
kw
.
update
({
"count"
:
1
,
"limit"
:
0
})
return
self
.
model_class
.
GET
(
self
.
model_class
.
ENDPOINT_ROOT
,
**
kw
)
.
get
(
'count'
)
kw
.
update
({
"count"
:
1
})
return
self
.
model_class
.
GET
(
self
.
model_class
.
ENDPOINT_ROOT
,
**
kw
)
.
get
(
'count'
)
def
all
(
self
):
return
Queryset
(
self
)
...
...
@@ -58,31 +53,15 @@ class QueryManager(object):
return
self
.
filter
(
**
kw
)
.
get
()
class
QuerysetMetaclass
(
type
):
"""metaclass to add the dynamically generated comparison functions"""
def
__new__
(
cls
,
name
,
bases
,
dct
):
cls
=
super
(
QuerysetMetaclass
,
cls
)
.
__new__
(
cls
,
name
,
bases
,
dct
)
for
fname
in
[
'limit'
,
'skip'
]:
def
func
(
self
,
value
,
fname
=
fname
):
s
=
copy
.
deepcopy
(
self
)
s
.
_options
[
fname
]
=
int
(
value
)
return
s
setattr
(
cls
,
fname
,
func
)
return
cls
class
Queryset
(
object
):
__metaclass__
=
QuerysetMetaclass
OPERATORS
=
[
'lt'
,
'lte'
,
'gt'
,
'gte'
,
'ne'
,
'in'
,
'nin'
,
'exists'
,
'select'
,
'dontSelect'
,
'all'
,
'relatedTo'
]
]
@staticmethod
def
convert_to_parse
(
value
):
from
datatypes
import
ParseType
from
parse_rest.
datatypes
import
ParseType
return
ParseType
.
convert_to_parse
(
value
,
as_pointer
=
True
)
@classmethod
...
...
@@ -100,11 +79,20 @@ class Queryset(object):
self
.
_options
=
{}
self
.
_result_cache
=
None
def
__deepcopy__
(
self
,
memo
):
q
=
self
.
__class__
(
self
.
_manager
)
q
.
_where
=
copy
.
deepcopy
(
self
.
_where
,
memo
)
q
.
_options
=
copy
.
deepcopy
(
self
.
_options
,
memo
)
q
.
_select_related
.
extend
(
self
.
_select_related
)
return
q
def
__iter__
(
self
):
return
iter
(
self
.
_fetch
())
def
__len__
(
self
):
return
self
.
_fetch
(
count
=
True
)
#don't use count query for len operator
#count doesn't return real size of result in all cases (eg if query contains skip option)
return
len
(
self
.
_fetch
())
def
__getitem__
(
self
,
key
):
if
isinstance
(
key
,
slice
):
...
...
@@ -112,7 +100,7 @@ class Queryset(object):
return
self
.
_fetch
()[
key
]
def
_fetch
(
self
,
count
=
False
):
if
self
.
_result_cache
:
if
self
.
_result_cache
is
not
None
:
return
len
(
self
.
_result_cache
)
if
count
else
self
.
_result_cache
"""
Return a list of objects matching query, or if count == True return
...
...
@@ -131,33 +119,43 @@ class Queryset(object):
return
self
.
_result_cache
def
filter
(
self
,
**
kw
):
q
=
copy
.
deepcopy
(
self
)
for
name
,
value
in
kw
.
items
():
parse_value
=
Queryset
.
convert_to_parse
(
value
)
attr
,
operator
=
Queryset
.
extract_filter_operator
(
name
)
if
operator
is
None
:
self
.
_where
[
attr
]
=
parse_value
q
.
_where
[
attr
]
=
parse_value
elif
operator
==
'relatedTo'
:
self
.
_where
[
'$'
+
operator
]
=
parse_value
q
.
_where
[
'$'
+
operator
]
=
parse_value
else
:
try
:
self
.
_where
[
attr
][
'$'
+
operator
]
=
parse_value
except
TypeError
:
# self._where[attr] wasn't settable
raise
ValueError
(
"Cannot filter for a constraint "
+
"after filtering for a specific value"
)
return
self
if
not
isinstance
(
q
.
_where
[
attr
],
dict
):
q
.
_where
[
attr
]
=
{}
q
.
_where
[
attr
][
'$'
+
operator
]
=
parse_value
return
q
def
limit
(
self
,
value
):
q
=
copy
.
deepcopy
(
self
)
q
.
_options
[
'limit'
]
=
int
(
value
)
return
q
def
skip
(
self
,
value
):
q
=
copy
.
deepcopy
(
self
)
q
.
_options
[
'skip'
]
=
int
(
value
)
return
q
def
order_by
(
self
,
order
,
descending
=
False
):
q
=
copy
.
deepcopy
(
self
)
# add a minus sign before the order value if descending == True
self
.
_options
[
'order'
]
=
descending
and
(
'-'
+
order
)
or
order
return
self
q
.
_options
[
'order'
]
=
descending
and
(
'-'
+
order
)
or
order
return
q
def
select_related
(
self
,
*
fields
):
self
.
_select_related
.
extend
(
fields
)
return
self
q
=
copy
.
deepcopy
(
self
)
q
.
_select_related
.
extend
(
fields
)
return
q
def
count
(
self
):
return
len
(
self
)
return
self
.
_fetch
(
count
=
True
)
def
exists
(
self
):
return
bool
(
self
)
...
...
@@ -171,4 +169,4 @@ class Queryset(object):
return
results
[
0
]
def
__repr__
(
self
):
return
unicode
(
self
.
_fetch
())
return
repr
(
self
.
_fetch
())
parse_rest/tests.py
View file @
ffacfc4b
...
...
@@ -4,20 +4,22 @@
"""
Contains unit tests for the Python Parse REST API wrapper
"""
from
__future__
import
print_function
import
os
import
sys
import
subprocess
import
unittest
import
datetime
import
six
from
itertools
import
chain
from
core
import
ResourceRequestNotFound
from
connection
import
register
,
ParseBatcher
from
datatypes
import
GeoPoint
,
Object
,
Function
from
user
import
User
import
query
from
installation
import
Push
from
parse_rest.core
import
ResourceRequestNotFound
from
parse_rest.connection
import
register
,
ParseBatcher
from
parse_rest.datatypes
import
GeoPoint
,
Object
,
Function
from
parse_rest.user
import
User
from
parse_rest
import
query
from
parse_rest.installation
import
Push
try
:
import
settings_local
...
...
@@ -25,17 +27,12 @@ except ImportError:
sys
.
exit
(
'You must create a settings_local.py file with APPLICATION_ID, '
\
'REST_API_KEY, MASTER_KEY variables set'
)
try
:
unicode
=
unicode
except
NameError
:
# is python3
unicode
=
str
register
(
getattr
(
settings_local
,
'APPLICATION_ID'
),
getattr
(
settings_local
,
'REST_API_KEY'
),
master_key
=
getattr
(
settings_local
,
'MASTER_KEY'
)
)
)
GLOBAL_JSON_TEXT
=
"""{
"applications": {
...
...
@@ -76,67 +73,63 @@ class CollectedItem(Object):
class
TestObject
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
score
=
GameScore
(
score
=
1337
,
player_name
=
'John Doe'
,
cheat_mode
=
False
)
self
.
sao_paulo
=
City
(
name
=
'São Paulo'
,
location
=
GeoPoint
(
-
23.5
,
-
46.6167
)
)
self
.
score
=
GameScore
(
score
=
1337
,
player_name
=
'John Doe'
,
cheat_mode
=
False
)
self
.
sao_paulo
=
City
(
name
=
'São Paulo'
,
location
=
GeoPoint
(
-
23.5
,
-
46.6167
))
def
tearDown
(
self
):
city_name
=
getattr
(
self
.
sao_paulo
,
'name'
,
None
)
game_score
=
getattr
(
self
.
score
,
'score'
,
None
)
if
city_name
:
for
city
in
City
.
Query
.
filter
(
name
=
city_name
):
city
.
delete
()
ParseBatcher
()
.
batch_delete
(
City
.
Query
.
filter
(
name
=
city_name
))
if
game_score
:
for
score
in
GameScore
.
Query
.
filter
(
score
=
game_score
):
score
.
delete
()
ParseBatcher
()
.
batch_delete
(
GameScore
.
Query
.
filter
(
score
=
game_score
))
def
testCanInitialize
(
self
):
self
.
assert
_
(
self
.
score
.
score
==
1337
,
'Could not set score'
)
self
.
assert
Equal
(
self
.
score
.
score
,
1337
,
'Could not set score'
)
def
testCanInstantiateParseType
(
self
):
self
.
assert_
(
self
.
sao_paulo
.
location
.
latitude
==
-
23.5
)
self
.
assertEqual
(
self
.
sao_paulo
.
location
.
latitude
,
-
23.5
)
def
testFactory
(
self
):
self
.
assertEqual
(
Object
.
factory
(
'_User'
),
User
)
self
.
assertEqual
(
Object
.
factory
(
'GameScore'
),
GameScore
)
def
testCanSaveDates
(
self
):
now
=
datetime
.
datetime
.
now
()
self
.
score
.
last_played
=
now
self
.
score
.
save
()
self
.
assert
_
(
self
.
score
.
last_played
==
now
,
'Could not save date'
)
self
.
assert
Equal
(
self
.
score
.
last_played
,
now
,
'Could not save date'
)
def
testCanCreateNewObject
(
self
):
self
.
score
.
save
()
object_id
=
self
.
score
.
objectId
self
.
assert_
(
object_id
is
not
None
,
'Can not create object'
)
self
.
assert_
(
type
(
object_id
)
==
unicode
)
self
.
assert_
(
type
(
self
.
score
.
createdAt
)
==
datetime
.
datetime
)
self
.
assert_
(
GameScore
.
Query
.
filter
(
objectId
=
object_id
)
.
exists
(),
'Can not create object'
)
self
.
assertIsNotNone
(
object_id
,
'Can not create object'
)
self
.
assertIsInstance
(
object_id
,
six
.
string_types
)
self
.
assertIsInstance
(
self
.
score
.
createdAt
,
datetime
.
datetime
)
self
.
assertTrue
(
GameScore
.
Query
.
filter
(
objectId
=
object_id
)
.
exists
(),
'Can not create object'
)
def
testCanUpdateExistingObject
(
self
):
self
.
sao_paulo
.
save
()
self
.
sao_paulo
.
country
=
'Brazil'
self
.
sao_paulo
.
save
()
self
.
assert
_
(
type
(
self
.
sao_paulo
.
updatedAt
)
==
datetime
.
datetime
)
self
.
assert
IsInstance
(
self
.
sao_paulo
.
updatedAt
,
datetime
.
datetime
)
city
=
City
.
Query
.
get
(
name
=
'São Paulo'
)
self
.
assert
_
(
city
.
country
==
'Brazil'
,
'Could not update object'
)
self
.
assert
Equal
(
city
.
country
,
'Brazil'
,
'Could not update object'
)
def
testCanDeleteExistingObject
(
self
):
self
.
score
.
save
()
object_id
=
self
.
score
.
objectId
self
.
score
.
delete
()
self
.
assert
_
(
not
GameScore
.
Query
.
filter
(
objectId
=
object_id
)
.
exists
(),
'Failed to delete object
%
s on Parse '
%
self
.
score
)
self
.
assert
False
(
GameScore
.
Query
.
filter
(
objectId
=
object_id
)
.
exists
(),
'Failed to delete object
%
s on Parse '
%
self
.
score
)
def
testCanIncrementField
(
self
):
previous_score
=
self
.
score
.
score
self
.
score
.
save
()
self
.
score
.
increment
(
'score'
)
self
.
assert
_
(
GameScore
.
Query
.
filter
(
score
=
previous_score
+
1
)
.
exists
(),
self
.
assert
True
(
GameScore
.
Query
.
filter
(
score
=
previous_score
+
1
)
.
exists
(),
'Failed to increment score on backend'
)
def
testAssociatedObject
(
self
):
...
...
@@ -149,20 +142,17 @@ class TestObject(unittest.TestCase):
# get the object, see if it has saved
qs
=
GameScore
.
Query
.
get
(
objectId
=
self
.
score
.
objectId
)
self
.
assert_
(
isinstance
(
qs
.
item
,
Object
),
"Associated CollectedItem is not an object"
)
self
.
assert_
(
qs
.
item
.
type
==
"Sword"
,
"Associated CollectedItem does not have correct attributes"
)
self
.
assertIsInstance
(
qs
.
item
,
CollectedItem
)
self
.
assertEqual
(
qs
.
item
.
type
,
"Sword"
,
"Associated CollectedItem does not have correct attributes"
)
def
testBatch
(
self
):
"""test saving, updating and deleting objects in batches"""
scores
=
[
GameScore
(
score
=
s
,
player_name
=
'Jane'
,
cheat_mode
=
False
)
for
s
in
range
(
5
)]
scores
=
[
GameScore
(
score
=
s
,
player_name
=
'Jane'
,
cheat_mode
=
False
)
for
s
in
range
(
5
)]
batcher
=
ParseBatcher
()
batcher
.
batch_save
(
scores
)
self
.
assert
_
(
GameScore
.
Query
.
filter
(
player_name
=
'Jane'
)
.
count
()
==
5
,
self
.
assert
Equal
(
GameScore
.
Query
.
filter
(
player_name
=
'Jane'
)
.
count
(),
5
,
"batch_save didn't create objects"
)
self
.
assert
_
(
all
(
s
.
objectId
is
not
None
for
s
in
scores
),
self
.
assert
True
(
all
(
s
.
objectId
is
not
None
for
s
in
scores
),
"batch_save didn't record object IDs"
)
# test updating
...
...
@@ -172,11 +162,11 @@ class TestObject(unittest.TestCase):
updated_scores
=
GameScore
.
Query
.
filter
(
player_name
=
'Jane'
)
self
.
assertEqual
(
sorted
([
s
.
score
for
s
in
updated_scores
]),
range
(
10
,
15
),
msg
=
"batch_save didn't update objects"
)
list
(
range
(
10
,
15
)
),
msg
=
"batch_save didn't update objects"
)
# test deletion
batcher
.
batch_delete
(
scores
)
self
.
assert
_
(
GameScore
.
Query
.
filter
(
player_name
=
'Jane'
)
.
count
()
==
0
,
self
.
assert
Equal
(
GameScore
.
Query
.
filter
(
player_name
=
'Jane'
)
.
count
(),
0
,
"batch_delete didn't delete objects"
)
...
...
@@ -186,72 +176,83 @@ class TestTypes(unittest.TestCase):
self
.
score
=
GameScore
(
score
=
1337
,
player_name
=
'John Doe'
,
cheat_mode
=
False
,
date_of_birth
=
self
.
now
)
)
self
.
sao_paulo
=
City
(
name
=
'São Paulo'
,
location
=
GeoPoint
(
-
23.5
,
-
46.6167
)
)
)
def
testCanConvertToNative
(
self
):
native_data
=
self
.
sao_paulo
.
_to_native
()
self
.
assert
_
(
type
(
native_data
)
is
dict
,
'Can not convert object to dict'
)
self
.
assert
IsInstance
(
native_data
,
dict
,
'Can not convert object to dict'
)
def
testCanConvertNestedLocation
(
self
):
native_sao_paulo
=
self
.
sao_paulo
.
_to_native
()
location_dict
=
native_sao_paulo
.
get
(
'location'
)
self
.
assert
_
(
type
(
location_dict
)
is
dict
,
'Expected dict after conversion. Got
%
s'
%
location_dict
)
self
.
assert
_
(
location_dict
.
get
(
'latitude'
)
==
-
23.5
,
'Can not serialize geopoint data'
)
self
.
assert
IsInstance
(
location_dict
,
dict
,
'Expected dict after conversion. Got
%
s'
%
location_dict
)
self
.
assert
Equal
(
location_dict
.
get
(
'latitude'
),
-
23.5
,
'Can not serialize geopoint data'
)
def
testCanConvertDate
(
self
):
native_date
=
self
.
score
.
_to_native
()
.
get
(
'date_of_birth'
)
self
.
assert
_
(
type
(
native_date
)
is
dict
,
'Could not serialize date into dict'
)
self
.
assert
IsInstance
(
native_date
,
dict
,
'Could not serialize date into dict'
)
iso_date
=
native_date
.
get
(
'iso'
)
now
=
'{0}Z'
.
format
(
self
.
now
.
isoformat
()[:
-
3
])
self
.
assert
_
(
iso_date
==
now
,
'Expected
%
s. Got
%
s'
%
(
now
,
iso_date
))
self
.
assert
Equal
(
iso_date
,
now
,
'Expected
%
s. Got
%
s'
%
(
now
,
iso_date
))
class
TestQuery
(
unittest
.
TestCase
):
"""Tests of an object's Queryset"""
def
setUp
(
self
):
@classmethod
def
setUpClass
(
cls
):
"""save a bunch of GameScore objects with varying scores"""
# first delete any that exist
for
s
in
GameScore
.
Query
.
all
():
s
.
delete
()
for
g
in
Game
.
Query
.
all
():
g
.
delete
()
ParseBatcher
()
.
batch_delete
(
GameScore
.
Query
.
all
())
ParseBatcher
()
.
batch_delete
(
Game
.
Query
.
all
())
self
.
game
=
Game
(
title
=
"Candyland"
)
self
.
game
.
save
()
cls
.
game
=
Game
(
title
=
"Candyland"
)
cls
.
game
.
save
()
self
.
scores
=
[
GameScore
(
score
=
s
,
player_name
=
'John Doe'
,
game
=
self
.
game
)
for
s
in
range
(
1
,
6
)]
for
s
in
self
.
scores
:
s
.
save
()
cls
.
scores
=
[
GameScore
(
score
=
s
,
player_name
=
'John Doe'
,
game
=
cls
.
game
)
for
s
in
range
(
1
,
6
)]
ParseBatcher
()
.
batch_save
(
cls
.
scores
)
@classmethod
def
tearDownClass
(
cls
):
'''delete all GameScore and Game objects'''
ParseBatcher
()
.
batch_delete
(
chain
(
cls
.
scores
,
[
cls
.
game
]))
def
setUp
(
self
):
self
.
test_objects
=
[]
def
tearDown
(
self
):
'''delete additional helper objects created in perticular tests'''
if
self
.
test_objects
:
ParseBatcher
()
.
batch_delete
(
self
.
test_objects
)
self
.
test_objects
=
[]
def
testExists
(
self
):
"""test the Queryset.exists() method"""
for
s
in
range
(
1
,
6
):
self
.
assert
_
(
GameScore
.
Query
.
filter
(
score
=
s
)
.
exists
(),
"exists giving false negative"
)
self
.
assert
_
(
not
GameScore
.
Query
.
filter
(
score
=
10
)
.
exists
(),
"exists giving false positive"
)
self
.
assert
True
(
GameScore
.
Query
.
filter
(
score
=
s
)
.
exists
(),
"exists giving false negative"
)
self
.
assert
False
(
GameScore
.
Query
.
filter
(
score
=
10
)
.
exists
(),
"exists giving false positive"
)
def
testCanFilter
(
self
):
'''test the Queryset.filter() method'''
for
s
in
self
.
scores
:
qobj
=
GameScore
.
Query
.
filter
(
objectId
=
s
.
objectId
)
.
get
()
self
.
assert
_
(
qobj
.
objectId
==
s
.
objectId
,
"Getting object with .filter() failed"
)
self
.
assert
_
(
qobj
.
score
==
s
.
score
,
"Getting object with .filter() failed"
)
self
.
assert
Equal
(
qobj
.
objectId
,
s
.
objectId
,
"Getting object with .filter() failed"
)
self
.
assert
Equal
(
qobj
.
score
,
s
.
score
,
"Getting object with .filter() failed"
)
# test relational query with other Objects
num_scores
=
GameScore
.
Query
.
filter
(
game
=
self
.
game
)
.
count
()
self
.
assert
_
(
num_scores
==
len
(
self
.
scores
),
self
.
assert
True
(
num_scores
==
len
(
self
.
scores
),
"Relational query with .filter() failed"
)
def
testGetExceptions
(
self
):
...
...
@@ -265,65 +266,78 @@ class TestQuery(unittest.TestCase):
last_week
=
datetime
.
datetime
.
now
()
-
datetime
.
timedelta
(
days
=
7
)
score
=
GameScore
(
name
=
'test'
,
last_played
=
last_week
)
score
.
save
()
self
.
assert_
(
GameScore
.
Query
.
filter
(
last_played
=
last_week
)
.
exists
(),
'Could not run query with dates'
)
self
.
test_objects
.
append
(
score
)
self
.
assertTrue
(
GameScore
.
Query
.
filter
(
last_played
=
last_week
)
.
exists
(),
'Could not run query with dates'
)
def
testComparisons
(
self
):
"""test comparison operators- gt, gte, lt, lte, ne"""
scores_gt_3
=
list
(
GameScore
.
Query
.
filter
(
score__gt
=
3
)
)
scores_gt_3
=
GameScore
.
Query
.
filter
(
score__gt
=
3
)
self
.
assertEqual
(
len
(
scores_gt_3
),
2
)
self
.
assert
_
(
all
([
s
.
score
>
3
for
s
in
scores_gt_3
]))
self
.
assert
True
(
all
([
s
.
score
>
3
for
s
in
scores_gt_3
]))
scores_gte_3
=
list
(
GameScore
.
Query
.
filter
(
score__gte
=
3
)
)
scores_gte_3
=
GameScore
.
Query
.
filter
(
score__gte
=
3
)
self
.
assertEqual
(
len
(
scores_gte_3
),
3
)
self
.
assert
_
(
all
([
s
.
score
>=
3
for
s
in
scores_gt_3
]))
self
.
assert
True
(
all
([
s
.
score
>=
3
for
s
in
scores_gt_3
]))
scores_lt_4
=
list
(
GameScore
.
Query
.
filter
(
score__lt
=
4
)
)
scores_lt_4
=
GameScore
.
Query
.
filter
(
score__lt
=
4
)
self
.
assertEqual
(
len
(
scores_lt_4
),
3
)
self
.
assert
_
(
all
([
s
.
score
<
4
for
s
in
scores_lt_4
]))
self
.
assert
True
(
all
([
s
.
score
<
4
for
s
in
scores_lt_4
]))
scores_lte_4
=
list
(
GameScore
.
Query
.
filter
(
score__lte
=
4
)
)
scores_lte_4
=
GameScore
.
Query
.
filter
(
score__lte
=
4
)
self
.
assertEqual
(
len
(
scores_lte_4
),
4
)
self
.
assert
_
(
all
([
s
.
score
<=
4
for
s
in
scores_lte_4
]))
self
.
assert
True
(
all
([
s
.
score
<=
4
for
s
in
scores_lte_4
]))
scores_ne_2
=
list
(
GameScore
.
Query
.
filter
(
score__ne
=
2
)
)
scores_ne_2
=
GameScore
.
Query
.
filter
(
score__ne
=
2
)
self
.
assertEqual
(
len
(
scores_ne_2
),
4
)
self
.
assert_
(
all
([
s
.
score
!=
2
for
s
in
scores_ne_2
]))
self
.
assertTrue
(
all
([
s
.
score
!=
2
for
s
in
scores_ne_2
]))
def
testChaining
(
self
):
lt_4_gt_2
=
GameScore
.
Query
.
filter
(
score__lt
=
4
)
.
filter
(
score__gt
=
2
)
self
.
assertEqual
(
len
(
lt_4_gt_2
),
1
,
'chained lt+gt not working'
)
self
.
assertEqual
(
lt_4_gt_2
[
0
]
.
score
,
3
,
'chained lt+gt not working'
)
# test chaining
lt_4_gt_2
=
list
(
GameScore
.
Query
.
filter
(
score__lt
=
4
)
.
filter
(
score__gt
=
2
))
self
.
assert_
(
len
(
lt_4_gt_2
)
==
1
,
'chained lt+gt not working'
)
self
.
assert_
(
lt_4_gt_2
[
0
]
.
score
==
3
,
'chained lt+gt not working'
)
q
=
GameScore
.
Query
.
filter
(
score__gt
=
3
,
score__lt
=
3
)
self
.
assert_
(
not
q
.
exists
(),
"chained lt+gt not working"
)
self
.
assertFalse
(
q
.
exists
(),
"chained lt+gt not working"
)
# test original queries are idependent after filting
q_all
=
GameScore
.
Query
.
all
()
q_special
=
q_all
.
filter
(
score__gt
=
3
)
self
.
assertEqual
(
len
(
q_all
),
5
)
self
.
assertEqual
(
len
(
q_special
),
2
)
def
testOptions
(
self
):
q_all
=
GameScore
.
Query
.
all
()
q_limit
=
q_all
.
limit
(
1
)
self
.
assertEqual
(
len
(
q_all
),
5
)
self
.
assertEqual
(
len
(
q_limit
),
1
)
def
testOrderBy
(
self
):
"""test three options- order, limit, and skip"""
scores_ordered
=
list
(
GameScore
.
Query
.
all
()
.
order_by
(
"score"
))
self
.
assertEqual
([
s
.
score
for
s
in
scores_ordered
],
[
1
,
2
,
3
,
4
,
5
])
scores_ordered
=
GameScore
.
Query
.
all
()
.
order_by
(
"score"
)
self
.
assertEqual
([
s
.
score
for
s
in
scores_ordered
],
[
1
,
2
,
3
,
4
,
5
])
scores_ordered_desc
=
GameScore
.
Query
.
all
()
.
order_by
(
"score"
,
descending
=
True
)
self
.
assertEqual
([
s
.
score
for
s
in
scores_ordered_desc
],
[
5
,
4
,
3
,
2
,
1
])
scores_ordered_desc
=
list
(
GameScore
.
Query
.
all
()
.
order_by
(
"score"
,
descending
=
True
))
self
.
assertEqual
([
s
.
score
for
s
in
scores_ordered_desc
],
[
5
,
4
,
3
,
2
,
1
]
)
def
testLimit
(
self
):
q
=
GameScore
.
Query
.
all
()
.
limit
(
3
)
self
.
assertEqual
(
len
(
q
),
3
)
scores_limit_3
=
list
(
GameScore
.
Query
.
all
()
.
limit
(
3
))
self
.
assert_
(
len
(
scores_limit_3
)
==
3
,
"Limit did not return 3 items"
)
def
testSkip
(
self
):
q
=
GameScore
.
Query
.
all
()
.
skip
(
3
)
self
.
assertEqual
(
len
(
q
),
2
)
scores_skip_3
=
list
(
GameScore
.
Query
.
all
()
.
skip
(
3
))
self
.
assert_
(
len
(
scores_skip_3
)
==
2
,
"Skip did not return 2 items"
)
def
testSelectRelated
(
self
):
score
=
GameScore
.
Query
.
all
()
.
select_related
(
'game'
)
.
limit
(
1
)[
0
]
self
.
assertTrue
(
score
.
game
.
objectId
)
#nice to have - also check no more then one query is triggered
def
testCanCompareDateInequality
(
self
):
today
=
datetime
.
datetime
.
today
()
tomorrow
=
today
+
datetime
.
timedelta
(
days
=
1
)
self
.
assert_
(
GameScore
.
Query
.
filter
(
createdAt__lte
=
tomorrow
)
.
count
()
==
5
,
'Could not make inequality comparison with dates'
)
def
tearDown
(
self
):
'''delete all GameScore and Game objects'''
for
s
in
GameScore
.
Query
.
all
():
s
.
delete
()
self
.
game
.
delete
()
self
.
assertEqual
(
GameScore
.
Query
.
filter
(
createdAt__lte
=
tomorrow
)
.
count
(),
5
,
'Could not make inequality comparison with dates'
)
class
TestFunction
(
unittest
.
TestCase
):
...
...
@@ -346,8 +360,7 @@ class TestFunction(unittest.TestCase):
os
.
chdir
(
original_dir
)
def
tearDown
(
self
):
for
review
in
Review
.
Query
.
all
():
review
.
delete
()
ParseBatcher
()
.
batch_delete
(
Review
.
Query
.
all
())
def
test_simple_functions
(
self
):
"""test hello world and averageStars functions"""
...
...
@@ -407,13 +420,13 @@ class TestUser(unittest.TestCase):
def
testCanSignUp
(
self
):
self
.
_destroy_user
()
user
=
User
.
signup
(
self
.
username
,
self
.
password
)
self
.
assert
_
(
user
is
not
None
)
self
.
assert
_
(
user
.
username
==
self
.
username
)
self
.
assert
IsNotNone
(
user
)
self
.
assert
Equal
(
user
.
username
,
self
.
username
)
def
testCanLogin
(
self
):
self
.
_get_user
()
# User should be created here.
user
=
User
.
login
(
self
.
username
,
self
.
password
)
self
.
assert
_
(
user
.
is_authenticated
(),
'Login failed'
)
self
.
assert
True
(
user
.
is_authenticated
(),
'Login failed'
)
def
testCanUpdate
(
self
):
user
=
self
.
_get_logged_user
()
...
...
@@ -423,7 +436,7 @@ class TestUser(unittest.TestCase):
user
.
phone
=
phone_number
user
.
save
()
self
.
assert
_
(
User
.
Query
.
filter
(
phone
=
phone_number
)
.
exists
(),
self
.
assert
True
(
User
.
Query
.
filter
(
phone
=
phone_number
)
.
exists
(),
'Failed to update user data. New info not on Parse'
)
def
testCanBatchUpdate
(
self
):
...
...
@@ -436,10 +449,10 @@ class TestUser(unittest.TestCase):
batcher
=
ParseBatcher
()
batcher
.
batch_save
([
user
])
self
.
assert
_
(
User
.
Query
.
filter
(
phone
=
phone_number
)
.
exists
(),
'Failed to batch update user data. New info not on Parse'
)
self
.
assert
_
(
user
.
updatedAt
!=
original_updatedAt
,
'Failed to batch update user data: updatedAt not changed'
)
self
.
assert
True
(
User
.
Query
.
filter
(
phone
=
phone_number
)
.
exists
(),
'Failed to batch update user data. New info not on Parse'
)
self
.
assert
NotEqual
(
user
.
updatedAt
,
original_updatedAt
,
'Failed to batch update user data: updatedAt not changed'
)
class
TestPush
(
unittest
.
TestCase
):
...
...
parse_rest/user.py
View file @
ffacfc4b
...
...
@@ -12,10 +12,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from
core
import
ResourceRequestLoginRequired
from
connection
import
API_ROOT
from
datatypes
import
ParseResource
,
ParseType
from
query
import
QueryManager
from
parse_rest.
core
import
ResourceRequestLoginRequired
from
parse_rest.
connection
import
API_ROOT
from
parse_rest.
datatypes
import
ParseResource
,
ParseType
from
parse_rest.
query
import
QueryManager
def
login_required
(
func
):
...
...
@@ -46,7 +46,7 @@ class User(ParseResource):
if
password
is
not
None
:
self
=
User
.
login
(
self
.
username
,
password
)
user
=
User
.
retrieve
(
self
.
objectId
)
user
=
User
.
Query
.
get
(
objectId
=
self
.
objectId
)
if
user
.
objectId
==
self
.
objectId
and
user
.
sessionToken
==
session_token
:
self
.
sessionToken
=
session_token
...
...
setup.py
View file @
ffacfc4b
...
...
@@ -27,6 +27,7 @@ setup(
url
=
'https://github.com/dgrtwo/ParsePy'
,
packages
=
[
'parse_rest'
],
package_data
=
{
"parse_rest"
:
[
os
.
path
.
join
(
"cloudcode"
,
"*"
,
"*"
)]},
install_requires
=
[
'six'
],
maintainer
=
'David Robinson'
,
maintainer_email
=
'dgrtwo@princeton.edu'
,
cmdclass
=
{
'test'
:
TestCommand
},
...
...
@@ -36,6 +37,9 @@ setup(
'Intended Audience :: Developers'
,
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)'
,
'Operating System :: OS Independent'
,
'Programming Language :: Python'
]
)
"Programming Language :: Python :: 2.6"
,
"Programming Language :: Python :: 2.7"
,
"Programming Language :: Python :: 3.3"
,
"Programming Language :: Python :: 3.4"
,
]
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment