Commit c6106771 by Roman Krejcik

Python3 support

parent cd9ef16a
...@@ -11,18 +11,13 @@ ...@@ -11,18 +11,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
try: from six.moves.urllib.request import Request, urlopen
from urllib2 import Request, urlopen, HTTPError from six.moves.urllib.error import HTTPError
from urllib import urlencode from six.moves.urllib.parse import urlencode
except ImportError:
# is Python3
from urllib.request import Request, urlopen
from urllib.error import HTTPError
from urllib.parse import urlencode
import json import json
import core from parse_rest import core
API_ROOT = 'https://api.parse.com/1' API_ROOT = 'https://api.parse.com/1'
ACCESS_KEYS = {} ACCESS_KEYS = {}
...@@ -79,6 +74,8 @@ class ParseBase(object): ...@@ -79,6 +74,8 @@ class ParseBase(object):
if http_verb == 'GET' and data: if http_verb == 'GET' and data:
url += '?%s' % urlencode(kw) url += '?%s' % urlencode(kw)
data = None data = None
else:
data = data.encode('utf-8')
request = Request(url, data, headers) request = Request(url, data, headers)
request.add_header('Content-type', 'application/json') request.add_header('Content-type', 'application/json')
...@@ -101,7 +98,7 @@ class ParseBase(object): ...@@ -101,7 +98,7 @@ class ParseBase(object):
}.get(e.code, core.ParseError) }.get(e.code, core.ParseError)
raise exc(e.read()) raise exc(e.read())
return json.loads(response.read()) return json.loads(response.read().decode('utf-8'))
@classmethod @classmethod
def GET(cls, uri, **kw): def GET(cls, uri, **kw):
......
...@@ -10,12 +10,14 @@ ...@@ -10,12 +10,14 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
import base64 import base64
import datetime import datetime
import six
from connection import API_ROOT, ParseBase from parse_rest.connection import API_ROOT, ParseBase
from query import QueryManager from parse_rest.query import QueryManager
class ParseType(object): class ParseType(object):
...@@ -94,7 +96,7 @@ class Pointer(ParseType): ...@@ -94,7 +96,7 @@ class Pointer(ParseType):
# also circular refs through more object are now ignored, in fact lazy loaded references will be best solution # also circular refs through more object are now ignored, in fact lazy loaded references will be best solution
objectData = dict(objectData) objectData = dict(objectData)
# now lets see if we have any references to the parent class here # now lets see if we have any references to the parent class here
for key, value in objectData.iteritems(): for key, value in six.iteritems(objectData):
if isinstance(value, dict) and "className" in value and value["className"] == parent_class_name: 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 # 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"] objectData[key] = value["objectId"]
...@@ -115,7 +117,6 @@ class Pointer(ParseType): ...@@ -115,7 +117,6 @@ class Pointer(ParseType):
return klass(**objectData) return klass(**objectData)
def __init__(self, obj): def __init__(self, obj):
self._object = obj self._object = obj
def _to_native(self): def _to_native(self):
...@@ -157,7 +158,7 @@ class Date(ParseType): ...@@ -157,7 +158,7 @@ class Date(ParseType):
"""Can be initialized either with a string or a datetime""" """Can be initialized either with a string or a datetime"""
if isinstance(date, datetime.datetime): if isinstance(date, datetime.datetime):
self._date = date self._date = date
elif isinstance(date, unicode): elif isinstance(date, six.string_types):
self._date = Date._from_str(date) self._date = Date._from_str(date)
def _to_native(self): def _to_native(self):
...@@ -247,7 +248,6 @@ class ParseResource(ParseBase, Pointer): ...@@ -247,7 +248,6 @@ class ParseResource(ParseBase, Pointer):
return dict([(k, v) for k, v in self.__dict__.items() if allowed(k)]) return dict([(k, v) for k, v in self.__dict__.items() if allowed(k)])
def __init__(self, **kw): def __init__(self, **kw):
for key, value in kw.items(): for key, value in kw.items():
setattr(self, key, ParseType.convert_from_parse(value, self.__class__.__name__)) setattr(self, key, ParseType.convert_from_parse(value, self.__class__.__name__))
...@@ -324,19 +324,21 @@ class ParseResource(ParseBase, Pointer): ...@@ -324,19 +324,21 @@ class ParseResource(ParseBase, Pointer):
updatedAt = property(_get_updated_datetime, _set_updated_datetime) updatedAt = property(_get_updated_datetime, _set_updated_datetime)
def __repr__(self): def __repr__(self):
return '<%s:%s>' % (unicode(self.__class__.__name__), self.objectId) return '<%s:%s>' % (self.__class__.__name__, self.objectId)
class ObjectMetaclass(type): class ObjectMetaclass(type):
def __new__(cls, name, bases, dct): def __new__(mcs, name, bases, dct):
cls = super(ObjectMetaclass, cls).__new__(cls, 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.set_endpoint_root()
cls.Query = QueryManager(cls) cls.Query = QueryManager(cls)
return cls return cls
class Object(ParseResource): class Object(six.with_metaclass(ObjectMetaclass, ParseResource)):
__metaclass__ = ObjectMetaclass
ENDPOINT_ROOT = '/'.join([API_ROOT, 'classes']) ENDPOINT_ROOT = '/'.join([API_ROOT, 'classes'])
@classmethod @classmethod
...@@ -357,7 +359,8 @@ class Object(ParseResource): ...@@ -357,7 +359,8 @@ class Object(ParseResource):
@property @property
def _absolute_url(self): def _absolute_url(self):
if not self.objectId: return None if not self.objectId:
return None
return '/'.join([self.__class__.ENDPOINT_ROOT, self.objectId]) return '/'.join([self.__class__.ENDPOINT_ROOT, self.objectId])
@property @property
......
...@@ -11,9 +11,9 @@ ...@@ -11,9 +11,9 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from connection import API_ROOT from parse_rest.connection import API_ROOT
from datatypes import ParseResource from parse_rest.datatypes import ParseResource
from query import QueryManager from parse_rest.query import QueryManager
class Installation(ParseResource): class Installation(ParseResource):
......
...@@ -14,11 +14,8 @@ ...@@ -14,11 +14,8 @@
import json import json
import collections import collections
import copy import copy
import six
try:
unicode = unicode
except NameError:
unicode = str
class QueryResourceDoesNotExist(Exception): class QueryResourceDoesNotExist(Exception):
'''Query returned no results''' '''Query returned no results'''
...@@ -42,8 +39,7 @@ class QueryManager(object): ...@@ -42,8 +39,7 @@ class QueryManager(object):
def _count(self, **kw): def _count(self, **kw):
kw.update({"count": 1, "limit": 0}) kw.update({"count": 1, "limit": 0})
return self.model_class.GET(self.model_class.ENDPOINT_ROOT, return self.model_class.GET(self.model_class.ENDPOINT_ROOT, **kw).get('count')
**kw).get('count')
def all(self): def all(self):
return Queryset(self) return Queryset(self)
...@@ -60,8 +56,8 @@ class QueryManager(object): ...@@ -60,8 +56,8 @@ class QueryManager(object):
class QuerysetMetaclass(type): class QuerysetMetaclass(type):
"""metaclass to add the dynamically generated comparison functions""" """metaclass to add the dynamically generated comparison functions"""
def __new__(cls, name, bases, dct): def __new__(mcs, name, bases, dct):
cls = super(QuerysetMetaclass, cls).__new__(cls, name, bases, dct) cls = super(QuerysetMetaclass, mcs).__new__(mcs, name, bases, dct)
for fname in ['limit', 'skip']: for fname in ['limit', 'skip']:
def func(self, value, fname=fname): def func(self, value, fname=fname):
...@@ -73,8 +69,7 @@ class QuerysetMetaclass(type): ...@@ -73,8 +69,7 @@ class QuerysetMetaclass(type):
return cls return cls
class Queryset(object): class Queryset(six.with_metaclass(QuerysetMetaclass, object)):
__metaclass__ = QuerysetMetaclass
OPERATORS = [ OPERATORS = [
'lt', 'lte', 'gt', 'gte', 'ne', 'in', 'nin', 'exists', 'select', 'dontSelect', 'all', 'relatedTo' 'lt', 'lte', 'gt', 'gte', 'ne', 'in', 'nin', 'exists', 'select', 'dontSelect', 'all', 'relatedTo'
...@@ -82,7 +77,7 @@ class Queryset(object): ...@@ -82,7 +77,7 @@ class Queryset(object):
@staticmethod @staticmethod
def convert_to_parse(value): def convert_to_parse(value):
from datatypes import ParseType from parse_rest.datatypes import ParseType
return ParseType.convert_to_parse(value, as_pointer=True) return ParseType.convert_to_parse(value, as_pointer=True)
@classmethod @classmethod
...@@ -143,8 +138,7 @@ class Queryset(object): ...@@ -143,8 +138,7 @@ class Queryset(object):
self._where[attr]['$' + operator] = parse_value self._where[attr]['$' + operator] = parse_value
except TypeError: except TypeError:
# self._where[attr] wasn't settable # self._where[attr] wasn't settable
raise ValueError("Cannot filter for a constraint " + raise ValueError("Cannot filter for a constraint after filtering for a specific value")
"after filtering for a specific value")
return self return self
def order_by(self, order, descending=False): def order_by(self, order, descending=False):
...@@ -171,4 +165,4 @@ class Queryset(object): ...@@ -171,4 +165,4 @@ class Queryset(object):
return results[0] return results[0]
def __repr__(self): def __repr__(self):
return unicode(self._fetch()) return repr(self._fetch())
...@@ -12,10 +12,10 @@ ...@@ -12,10 +12,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from core import ResourceRequestLoginRequired from parse_rest.core import ResourceRequestLoginRequired
from connection import API_ROOT from parse_rest.connection import API_ROOT
from datatypes import ParseResource, ParseType from parse_rest.datatypes import ParseResource, ParseType
from query import QueryManager from parse_rest.query import QueryManager
def login_required(func): def login_required(func):
......
...@@ -27,6 +27,7 @@ setup( ...@@ -27,6 +27,7 @@ setup(
url='https://github.com/dgrtwo/ParsePy', url='https://github.com/dgrtwo/ParsePy',
packages=['parse_rest'], packages=['parse_rest'],
package_data={"parse_rest": [os.path.join("cloudcode", "*", "*")]}, package_data={"parse_rest": [os.path.join("cloudcode", "*", "*")]},
install_requires=['six'],
maintainer='David Robinson', maintainer='David Robinson',
maintainer_email='dgrtwo@princeton.edu', maintainer_email='dgrtwo@princeton.edu',
cmdclass={'test': TestCommand}, cmdclass={'test': TestCommand},
...@@ -36,6 +37,9 @@ setup( ...@@ -36,6 +37,9 @@ setup(
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Operating System :: OS Independent', '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",
] ]
) )
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment