Skip to content

Commit 6242333

Browse files
committed
Merge branch 'v0.5.0-prep' and bump version to 0.5.0. Closes PR #21
2 parents 3593925 + e692ed8 commit 6242333

File tree

10 files changed

+257
-85
lines changed

10 files changed

+257
-85
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
*.pyc
2-
Embedly.egg-info/
2+
*.egg-info/
3+
.tox/
34
build/
45
dist/
56
.virtualenv

.travis.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ python:
44
- 2.7
55
- 3.2
66
- 3.3
7-
script: python embedly/tests.py
8-
install:
9-
- python setup.py -q install
7+
env:
8+
- PIP_USE_MIRRORS=true
9+
script: python setup.py test

README.rst

+58-33
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,59 @@
11
embedly-python
22
==============
3-
Python Library for interacting with Embedly's API. To get started sign up for
4-
a key at `embed.ly/signup <http://embed.ly/signup>`_.
3+
Python library for interacting with Embedly's API. To get started sign up for
4+
a key at `embed.ly/signup <https://app.embed.ly/signup>`_.
55

66
Install
77
-------
88
Install with `Pip <http://www.pip-installer.org>`_ (recommended)::
99

1010
pip install embedly
1111

12-
Or easy_install
12+
Or easy_install::
1313

14-
sudo easy_install Embedly
14+
easy_install Embedly
1515

1616
Or setuptools::
1717

1818
git clone git://github.com/embedly/embedly-python.git
19-
sudo python setup.py
19+
python setup.py
2020

21+
Setup requires Setuptools 0.7+ or Distribute 0.6.2+ in order to take advantage
22+
of the ``2to3`` option. Setup will still run on earlier versions but you'll
23+
see a warning and ``2to3`` won't happen. Read more in the Setuptools
24+
`docs <http://pythonhosted.org/setuptools/python3.html>`_
2125

2226
Getting Started
2327
---------------
2428
This library is meant to be a dead simple way to interact with the Embedly API.
25-
There are only 2 main objects, the ``Embedly`` client and the ``Url`` model.
26-
Here is a simple example and then we will go into the objects::
29+
There are only 2 main objects, the ``Embedly`` client and the ``Url`` response
30+
model. Here is a simple example and then we will go into the objects::
2731

2832
>>> from embedly import Embedly
2933
>>> client = Embedly(:key)
30-
>>> obj = client.oembed('http://instagr.am/p/BL7ti/')
34+
>>> obj = client.oembed('http://instagram.com/p/BL7ti/')
3135
>>> obj['type']
3236
u'photo'
3337
>>> obj['url']
34-
u'http://distillery.s3.amazonaws.com/media/2011/01/24/cdd759a319184cb79793506607ff5746_7.jpg'
38+
u'http://images.ak.instagram.com/media/2011/01/24/cdd759a319184cb79793506607ff5746_7.jpg'
3539

36-
>>> obj = client.oembed('http://instagr.am/p/error')
40+
>>> obj = client.oembed('http://instagram.com/error/error/')
3741
>>> obj['error']
3842
True
3943

4044
Embedly Client
4145
""""""""""""""
42-
The Embedly client is a object that takes in a key and an optional User Agent
43-
then handles all the interactions and HTTP requests to Embedly. To initialize
44-
the object pass in your key you got from signing up for Embedly and an optional
45-
User Agent.
46+
The Embedly client is a object that takes in a key and optional User Agent
47+
and timeout parameters then handles all the interactions and HTTP requests
48+
to Embedly. To initialize the object, you'll need the key that you got when
49+
you signed up for Embedly.
50+
::
4651

4752
>>> from embedly import Embedly
48-
>>> client = Embedly('key', 'Mozilla/5.0 (compatible; example-org;)')
53+
>>> client = Embedly('key')
54+
>>> client2 = Embedly('key', 'Mozilla/5.0 (compatible; example-org;)')
55+
>>> client3 = Embedly('key', 'Mozilla/5.0 (compatible; example-org;)', 30)
56+
>>> client4 = Embedly('key', timeout=10, user_agent='Mozilla/5.0 (compatible; example-org;)')
4957

5058
The client object now has a bunch of different methods that you can use.
5159

@@ -92,7 +100,7 @@ keyword arguments that correspond to Embedly's `query arguments
92100
>>> client.oembed(['http://vimeo.com/18150336',
93101
'http://www.youtube.com/watch?v=hD7ydlyhvKs'], maxwidth=500, words=20)
94102

95-
There are some supporting functions that allow you to limit urls before sending
103+
There are some supporting functions that allow you to limit URLs before sending
96104
them to Embedly. Embedly can return metadata for any URL, these just allow a
97105
developer to only pass a subset of Embedly `providers
98106
<http://embed.ly/providers>`_. Note that URL shorteners like bit.ly or t.co are
@@ -116,43 +124,60 @@ not supported through these regexes.
116124

117125
Url Object
118126
""""""""""
119-
The ``Url`` Object is just a smart dictionary that acts more like an object.
120-
For example when you run ``oembed`` you get back a Url Object:
127+
The ``Url`` object is basically a response dictionary returned from
128+
one of the Embedly API endpoints.
129+
::
121130

122-
>>> obj = client.oembed('http://vimeo.com/18150336', words=10)
131+
>>> response = client.oembed('http://vimeo.com/18150336', words=10)
123132

124-
Depending on the method you are using, the object has a different set of
133+
Depending on the method you are using, the response will have different
125134
attributes. We will go through a few, but you should read the `documentation
126-
<http://embed.ly/docs>`_ to get the full list of data that is passed back.::
135+
<http://embed.ly/docs>`_ to get the full list of data that is passed back.
136+
::
127137

128-
# Url Object can be accessed like a dictionary
129-
>>> obj['type']
138+
>>> response['type']
130139
u'video'
140+
>>> response['title']
141+
u'Wingsuit Basejumping - The Need 4 Speed: The Art of Flight'
142+
>>> response['provider_name']
143+
u'Vimeo'
144+
>>> response['width']
145+
1280
146+
147+
As you can see the ``Url`` object works like a dictionary, but it's slightly
148+
enhanced. It will always have ``method`` and ``original_url`` attributes,
149+
which represent the Embedly request type and the URL requested.
150+
::
151+
152+
>>> response.method
153+
'oembed'
154+
>>> response.original_url
155+
'http://vimeo.com/18150336'
131156

132-
# The url object always has an ``original_url`` attrbiute.
133-
>>> obj.original_url
134-
u'http://vimeo.com/18150336'
135-
# The method used to retrieve the URL is also on the obj
136-
>>> obj.method
137-
u'oembed'
157+
# useful because the response data itself may not have a URL
158+
# (or it could have a redirected link, querystring params, etc)
159+
>>> response['url']
160+
...
161+
KeyError: 'url'
138162

139-
For the Preview and Objectify endpoints the sub objects can also be accessed in
163+
For the Preview and Objectify endpoints the sub-objects can also be accessed in
140164
the same manner.
165+
::
141166

142167
>>> obj = client.preview('http://vimeo.com/18150336', words=10)
143168
>>> obj['object']['type']
144169
u'video'
145-
>>> obj['images'][0].url
170+
>>> obj['images'][0]['url']
146171
u'http://b.vimeocdn.com/ts/117/311/117311910_1280.jpg'
147172

148173
Error Handling
149174
--------------
150-
If there was an error processing the request, The ``Url`` object will contain
175+
If there was an error processing the request, the ``Url`` object will contain
151176
an error. For example if we use an invalid key, we will get a 401 response back
152177
::
153178

154179
>>> client = Embedly('notakey')
155-
>>> obj = client.preview('http://vimeo.com/18150336', words=10)
180+
>>> obj = client.preview('http://vimeo.com/18150336')
156181
>>> obj['error']
157182
True
158183
>>> obj['error_code']

embedly/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from __future__ import absolute_import
22
from .client import Embedly
33

4-
__version__ = '0.4.3'
4+
__version__ = '0.5.0'

embedly/client.py

+15-16
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44
55
The embedly object that interacts with the service
66
"""
7-
from __future__ import absolute_import
7+
from __future__ import absolute_import, unicode_literals
88
import re
99
import httplib2
1010
import json
11-
try:
12-
from urllib import quote, urlencode
13-
except ImportError:
14-
# py3k
15-
from urllib.parse import quote, urlencode
11+
from urllib import quote, urlencode
1612

1713
from .models import Url
1814

@@ -53,22 +49,24 @@ def get_services(self):
5349
the list of supported providers and their regexes
5450
"""
5551

56-
if self.services: return self.services
52+
if self.services:
53+
return self.services
5754

5855
url = 'http://api.embed.ly/1/services/python'
5956

6057
http = httplib2.Http(timeout=self.timeout)
61-
headers = {'User-Agent' : self.user_agent}
58+
headers = {'User-Agent': self.user_agent,
59+
'Connection': 'close'}
6260
resp, content = http.request(url, headers=headers)
6361

6462
if resp['status'] == '200':
65-
resp_data = json.loads(content)
63+
resp_data = json.loads(content.decode('utf-8'))
6664
self.services = resp_data
6765

68-
#build the regex that we can use later.
66+
# build the regex that we can use later
6967
_regex = []
70-
for each in self.get_services():
71-
_regex.append('|'.join(each.get('regex',[])))
68+
for each in self.services:
69+
_regex.append('|'.join(each.get('regex', [])))
7270

7371
self._regex = re.compile('|'.join(_regex))
7472

@@ -99,10 +97,10 @@ def _get(self, version, method, url_or_urls, **kwargs):
9997
raise ValueError('%s requires a url or a list of urls given: %s' %
10098
(method.title(), url_or_urls))
10199

102-
#A flag we can use instead of calling isinstance all the time.
100+
# a flag we can use instead of calling isinstance() all the time
103101
multi = isinstance(url_or_urls, list)
104102

105-
# Throw an error early for too many URLs
103+
# throw an error early for too many URLs
106104
if multi and len(url_or_urls) > 20:
107105
raise ValueError('Embedly accepts only 20 urls at a time. Url '
108106
'Count:%s' % len(url_or_urls))
@@ -111,7 +109,7 @@ def _get(self, version, method, url_or_urls, **kwargs):
111109

112110
key = kwargs.get('key', self.key)
113111

114-
#make sure that a key was set on the client or passed in.
112+
# make sure that a key was set on the client or passed in
115113
if not key:
116114
raise ValueError('Requires a key. None given: %s' % key)
117115

@@ -128,7 +126,8 @@ def _get(self, version, method, url_or_urls, **kwargs):
128126

129127
http = httplib2.Http(timeout=self.timeout)
130128

131-
headers = {'User-Agent': self.user_agent}
129+
headers = {'User-Agent': self.user_agent,
130+
'Connection': 'close'}
132131

133132
resp, content = http.request(url, headers=headers)
134133

embedly/models.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
from __future__ import unicode_literals
1+
from __future__ import absolute_import, unicode_literals
2+
from .py3_utils import python_2_unicode_compatible, IterableUserDict
23

3-
try:
4-
from UserDict import IterableUserDict
5-
except ImportError:
6-
from collections import UserDict as IterableUserDict
74

5+
@python_2_unicode_compatible
86
class Url(IterableUserDict, object):
97
"""
108
A dictionary with two additional attributes for the method and url.
@@ -18,7 +16,4 @@ def __init__(self, data=None, method=None, original_url=None, **kwargs):
1816
self.original_url = original_url
1917

2018
def __str__(self):
21-
return self.__unicode__().encode("utf-8")
22-
23-
def __unicode__(self):
2419
return '<%s %s>' % (self.method.title(), self.original_url or "")

embedly/py3_utils.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import sys
2+
3+
# 2to3 doesn't handle the UserDict relocation
4+
# put the import logic here for cleaner usage
5+
try:
6+
from collections import UserDict as IterableUserDict
7+
except ImportError: # Python 2
8+
from UserDict import IterableUserDict
9+
10+
11+
def python_2_unicode_compatible(klass):
12+
"""
13+
A decorator that defines __unicode__ and __str__ methods under Python 2.
14+
Under Python 3 it does nothing.
15+
16+
From django.utils.encoding.py in 1.4.2+, minus the dependency on Six.
17+
18+
To support Python 2 and 3 with a single code base, define a __str__ method
19+
returning text and apply this decorator to the class.
20+
"""
21+
if sys.version_info[0] == 2:
22+
if '__str__' not in klass.__dict__:
23+
raise ValueError("@python_2_unicode_compatible cannot be applied "
24+
"to %s because it doesn't define __str__()." %
25+
klass.__name__)
26+
klass.__unicode__ = klass.__str__
27+
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
28+
return klass

0 commit comments

Comments
 (0)