Skip to content

Commit

Permalink
Merge branch 'v0.5.0-prep' and bump version to 0.5.0. Closes PR #21
Browse files Browse the repository at this point in the history
  • Loading branch information
joncotton committed Nov 23, 2013
2 parents 3593925 + e692ed8 commit 6242333
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 85 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.pyc
Embedly.egg-info/
*.egg-info/
.tox/
build/
dist/
.virtualenv
Expand Down
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ python:
- 2.7
- 3.2
- 3.3
script: python embedly/tests.py
install:
- python setup.py -q install
env:
- PIP_USE_MIRRORS=true
script: python setup.py test
91 changes: 58 additions & 33 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,51 +1,59 @@
embedly-python
==============
Python Library for interacting with Embedly's API. To get started sign up for
a key at `embed.ly/signup <http://embed.ly/signup>`_.
Python library for interacting with Embedly's API. To get started sign up for
a key at `embed.ly/signup <https://app.embed.ly/signup>`_.

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

pip install embedly

Or easy_install
Or easy_install::

sudo easy_install Embedly
easy_install Embedly

Or setuptools::

git clone git://github.com/embedly/embedly-python.git
sudo python setup.py
python setup.py

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

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

>>> from embedly import Embedly
>>> client = Embedly(:key)
>>> obj = client.oembed('http://instagr.am/p/BL7ti/')
>>> obj = client.oembed('http://instagram.com/p/BL7ti/')
>>> obj['type']
u'photo'
>>> obj['url']
u'http://distillery.s3.amazonaws.com/media/2011/01/24/cdd759a319184cb79793506607ff5746_7.jpg'
u'http://images.ak.instagram.com/media/2011/01/24/cdd759a319184cb79793506607ff5746_7.jpg'

>>> obj = client.oembed('http://instagr.am/p/error')
>>> obj = client.oembed('http://instagram.com/error/error/')
>>> obj['error']
True

Embedly Client
""""""""""""""
The Embedly client is a object that takes in a key and an optional User Agent
then handles all the interactions and HTTP requests to Embedly. To initialize
the object pass in your key you got from signing up for Embedly and an optional
User Agent.
The Embedly client is a object that takes in a key and optional User Agent
and timeout parameters then handles all the interactions and HTTP requests
to Embedly. To initialize the object, you'll need the key that you got when
you signed up for Embedly.
::

>>> from embedly import Embedly
>>> client = Embedly('key', 'Mozilla/5.0 (compatible; example-org;)')
>>> client = Embedly('key')
>>> client2 = Embedly('key', 'Mozilla/5.0 (compatible; example-org;)')
>>> client3 = Embedly('key', 'Mozilla/5.0 (compatible; example-org;)', 30)
>>> client4 = Embedly('key', timeout=10, user_agent='Mozilla/5.0 (compatible; example-org;)')

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

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

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

Url Object
""""""""""
The ``Url`` Object is just a smart dictionary that acts more like an object.
For example when you run ``oembed`` you get back a Url Object:
The ``Url`` object is basically a response dictionary returned from
one of the Embedly API endpoints.
::

>>> obj = client.oembed('http://vimeo.com/18150336', words=10)
>>> response = client.oembed('http://vimeo.com/18150336', words=10)

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

# Url Object can be accessed like a dictionary
>>> obj['type']
>>> response['type']
u'video'
>>> response['title']
u'Wingsuit Basejumping - The Need 4 Speed: The Art of Flight'
>>> response['provider_name']
u'Vimeo'
>>> response['width']
1280

As you can see the ``Url`` object works like a dictionary, but it's slightly
enhanced. It will always have ``method`` and ``original_url`` attributes,
which represent the Embedly request type and the URL requested.
::

>>> response.method
'oembed'
>>> response.original_url
'http://vimeo.com/18150336'

# The url object always has an ``original_url`` attrbiute.
>>> obj.original_url
u'http://vimeo.com/18150336'
# The method used to retrieve the URL is also on the obj
>>> obj.method
u'oembed'
# useful because the response data itself may not have a URL
# (or it could have a redirected link, querystring params, etc)
>>> response['url']
...
KeyError: 'url'

For the Preview and Objectify endpoints the sub objects can also be accessed in
For the Preview and Objectify endpoints the sub-objects can also be accessed in
the same manner.
::

>>> obj = client.preview('http://vimeo.com/18150336', words=10)
>>> obj['object']['type']
u'video'
>>> obj['images'][0].url
>>> obj['images'][0]['url']
u'http://b.vimeocdn.com/ts/117/311/117311910_1280.jpg'

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

>>> client = Embedly('notakey')
>>> obj = client.preview('http://vimeo.com/18150336', words=10)
>>> obj = client.preview('http://vimeo.com/18150336')
>>> obj['error']
True
>>> obj['error_code']
Expand Down
2 changes: 1 addition & 1 deletion embedly/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import absolute_import
from .client import Embedly

__version__ = '0.4.3'
__version__ = '0.5.0'
31 changes: 15 additions & 16 deletions embedly/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@
The embedly object that interacts with the service
"""
from __future__ import absolute_import
from __future__ import absolute_import, unicode_literals
import re
import httplib2
import json
try:
from urllib import quote, urlencode
except ImportError:
# py3k
from urllib.parse import quote, urlencode
from urllib import quote, urlencode

from .models import Url

Expand Down Expand Up @@ -53,22 +49,24 @@ def get_services(self):
the list of supported providers and their regexes
"""

if self.services: return self.services
if self.services:
return self.services

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

http = httplib2.Http(timeout=self.timeout)
headers = {'User-Agent' : self.user_agent}
headers = {'User-Agent': self.user_agent,
'Connection': 'close'}
resp, content = http.request(url, headers=headers)

if resp['status'] == '200':
resp_data = json.loads(content)
resp_data = json.loads(content.decode('utf-8'))
self.services = resp_data

#build the regex that we can use later.
# build the regex that we can use later
_regex = []
for each in self.get_services():
_regex.append('|'.join(each.get('regex',[])))
for each in self.services:
_regex.append('|'.join(each.get('regex', [])))

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

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

#A flag we can use instead of calling isinstance all the time.
# a flag we can use instead of calling isinstance() all the time
multi = isinstance(url_or_urls, list)

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

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

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

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

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

headers = {'User-Agent': self.user_agent}
headers = {'User-Agent': self.user_agent,
'Connection': 'close'}

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

Expand Down
11 changes: 3 additions & 8 deletions embedly/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from __future__ import unicode_literals
from __future__ import absolute_import, unicode_literals
from .py3_utils import python_2_unicode_compatible, IterableUserDict

try:
from UserDict import IterableUserDict
except ImportError:
from collections import UserDict as IterableUserDict

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

def __str__(self):
return self.__unicode__().encode("utf-8")

def __unicode__(self):
return '<%s %s>' % (self.method.title(), self.original_url or "")
28 changes: 28 additions & 0 deletions embedly/py3_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import sys

# 2to3 doesn't handle the UserDict relocation
# put the import logic here for cleaner usage
try:
from collections import UserDict as IterableUserDict
except ImportError: # Python 2
from UserDict import IterableUserDict


def python_2_unicode_compatible(klass):
"""
A decorator that defines __unicode__ and __str__ methods under Python 2.
Under Python 3 it does nothing.
From django.utils.encoding.py in 1.4.2+, minus the dependency on Six.
To support Python 2 and 3 with a single code base, define a __str__ method
returning text and apply this decorator to the class.
"""
if sys.version_info[0] == 2:
if '__str__' not in klass.__dict__:
raise ValueError("@python_2_unicode_compatible cannot be applied "
"to %s because it doesn't define __str__()." %
klass.__name__)
klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass
Loading

2 comments on commit 6242333

@joncotton
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TravisCI status

@screeley you can hook up this repo to auto-push and test on Travis with these directions.

All that's left is the release on PyPI. Oh and to read over the release/tag text I wrote to make sure it's all correct.

@screeley
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do I'll take care of this Monday. Thank you so much.

Send me an email at sean at embed.ly, so I can send you some swag.

Please sign in to comment.