Git Product home page Git Product logo

pyactiveresource's Introduction

PyActiveResource

Build Status

A python port of the ActiveResource project, which provides an object-relational mapping for REST web services.

Development prior to 2014 took place here: https://code.google.com/p/pyactiveresource/

Development going forward will take place on Github. Please submit new issues and patches there.

pyactiveresource mailing list

Philosophy

Active Resource attempts to provide a coherent wrapper object-relational mapping for REST web services. It follows the same philosophy as Active Record, in that one of its prime aims is to reduce the amount of code needed to map to these resources. This is made possible by relying on a number of code- and protocol-based conventions that make it easy for Active Resource to infer complex relations and structures. These conventions are outlined in detail in the documentation for ActiveResource::Base.

Overview

Model classes are mapped to remote REST resources by Active Resource much the same way Active Record maps model classes to database tables. When a request is made to a remote resource, a REST JSON request is generated, transmitted, and the result received and serialized into a usable Ruby object.

Installation

To install or upgrade to the latest released version

pip install --upgrade pyactiveresource

To install this package from source

python setup.py install

Configuration and Usage

Using pyActiveResource is simple. First create a new class that inherits from and provide a site class variable to it:

# Import active resource
from pyactiveresource import activeresource as ar

# extend active resource and add the _site var to point to the REST endpoint
class Person(ar.ActiveResource):
  _site =  'http://api.people.com:3000/people'

Now the class is rest enabled each request is made on the class and responses spawn new object representing the response as a resource.

Authentication/Headers

You can set headers for each request with the _header class variable. This allows you to pass information to the REST endpoint such as auth tokens

class Person(ar.ActiveResource):
  _site =  'http://api.people.com:3000/people'
  _headers = { 'auth': token }

Protocol

pyActiveResource is built on a standard JSON format for requesting and submiting resources over HTTP. It mirrors the RESTful routing protocol. REST uses HTTP, but unlike "Typical" web applications, it makes uses of all available HTTP verbs from the specifaction.

  • GET requests are used to find and retrieve resources.
  • POST requests are used to create new resources.
  • PUT requests are used to update existing resources.
  • DELETE requests are used to delete resources.

For more information on how this protocol works see the article here.

Find

Find requests use the GET method and expect the JSON form of whatever resource/resources is/are being requested. So, for a request for a single element, the JSON of that item is expected in response:

# Expects a response of
#
# {"id":1,"first":"Tyler","last":"Durden"}
#
# for GET http://api.people.com:3000/people/1.json
#
person = Person.find(1) # person will be an object with the json keys mapped to attributes of the object

The JSON document that is received is used to build a new object of type Person, with each JSON element becoming an attribute on the object.

person.first # Tyler
type(person) # Person

Any complex element (one that contains other elements) becomes its own object:

# With this response:
# {"id":1,"first":"Tyler","address":{"street":"Paper St.","state":"CA"}}
#
# for GET http://api.people.com:3000/people/1.json
#
tyler = Person.find(1)
type(tyler.address)  # Address
tyler.address.street  # 'Paper St.'

Collections can also be requested in a similar fashion

# Expects a response of
#
# [
#   {"id":1,"first":"Tyler","last":"Durden"},
#   {"id":2,"first":"Tony","last":"Stark",}
# ]
#
# for GET http://api.people.com:3000/people.json
#
people = Person.find()
people[0].first  # 'Tyler'
people[1].first  # 'Tony

You can append url params to the request by passing in a dictionary of the required params.

# for GET http://api.people.com:3000/people.json?page=30&member=true
#
people = Person.find(page=30, member='true')

Create

Creating a new resource submits the JSON form of the resource as the body of the request and expects a 'Location' header in the response with the RESTful URL location of the newly created resource. The id of the newly created resource is parsed out of the Location response header and automatically set as the id of the object.

# {"first":"Tyler","last":"Durden"}
#
# is submitted as the body on
#
# POST http://api.people.com:3000/people.json
#
# when save is called on a new Person object.  An empty response is
# is expected with a 'Location' header value:
#
# Response (201): Location: http://api.people.com:3000/people/2
#
tyler = Person.create({ 'first': 'Tyler' })
tyler.id    # 2

Update

'save' is also used to update an existing resource and follows the same protocol as creating a resource with the exception that no response headers are needed – just an empty response when the update on the server side was successful.

# {"first":"Tyler"}
#
# is submitted as the body on
#
# PUT http://api.people.com:3000/people/1.json
#
# when save is called on an existing Person object.  An empty response is
# is expected with code (204)
#
tyler.first = 'Tyson'
tyler.save()  # true

Delete

Destruction of a resource can be invoked as a class and instance method of the resource.

# A request is made to
#
# DELETE http://api.people.com:3000/people/1.json
#
# for both of these forms.  An empty response with
# is expected with response code (200)
#
tyler.delete()

Running Tests

Run the following from the top level directory

python setup.py test

pyactiveresource's People

Contributors

cursedcoder avatar dylanahsmith avatar emdemir avatar gavinballard avatar gwincr11 avatar hockeybuggy avatar kevinhughes27 avatar mllemango avatar smubbs avatar tirkarthi avatar tylerball avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyactiveresource's Issues

Publish wheels

It would be great if this package published Python wheels, so that consumers didn't need to run the package's setup.py as part of their install process.

I think it could be as simple as adding bdist_wheel to the shipit.yml file, though you may also want to add:

[bdist_wheel]
universal = True

to your setup.cfg.

Not sure if I'm missing a reason that wheels wouldn't work for this project though?

TypeError when calling shopify.Resource.find with string param

Using django and python versions:

  • Django Version: 1.11.3
  • Python Version: 3.6.1

And with the following ShopifyAPI/pyactiveresource versions:

  • pyactiveresource==2.1.2
  • ShopifyAPI==2.5.0

The following code causes the error Exception Value: cannot use a string pattern on a bytes-like object.

    script_url = 'http://someurl.com/path/to.js'
    if not shopify.ScriptTag.find(src=script_url):
        script = shopify.ScriptTag()
        ···

Stack:

···

File "dev/lab/shopify-test/shopify_app/api/views.py" in auth
  58.     if not shopify.ScriptTag.find(src=script_url):

File "dev/env/shopify-test/lib/python3.6/site-packages/pyactiveresource/activeresource.py" in find
  385.         return cls._find_every(from_=from_, **kwargs)

File "dev/env/shopify-test/lib/python3.6/site-packages/pyactiveresource/activeresource.py" in _find_every
  515.         prefix_options, query_options = cls._split_options(kwargs)

File "dev/env/shopify-test/lib/python3.6/site-packages/pyactiveresource/activeresource.py" in _split_options
  466.             if key in cls._prefix_parameters():

File "dev/env/shopify-test/lib/python3.6/site-packages/pyactiveresource/activeresource.py" in _prefix_parameters
  720.         for match in template.pattern.finditer(path):

Exception Type: TypeError at /api/auth/
Exception Value: cannot use a string pattern on a bytes-like object

Testing with http_fake

Hey,

I'm not sure if the http_fake is designed to be used outside this project, but it looked like an easy way to mock for a python novice like me.

https://github.com/Shopify/pyactiveresource/blob/master/pyactiveresource/testing/http_fake.py#L21

This opener install expression seems to bleed outside the test and caused my functional tests' urllib go haywire.

I extended the test class in the Shopify python api to have reset the urllib opener at tearDown. That seemed to fix it. Is there a better way to solve this?

Pagination headers

Hello,

I am curious about support for pagination headers such as these:

$ curl --include 'https://localhost:3000/movies?page=5'
HTTP/1.1 200 OK
Link: <http://localhost:3000/movies?page=1>; rel="first",
  <http://localhost:3000/movies?page=173>; rel="last",
  <http://localhost:3000/movies?page=6>; rel="next",
  <http://localhost:3000/movies?page=4>; rel="prev"
Total: 4321
Per-Page: 10

I don't think this exists today, but it would be nice to add next page bindings to the api. Any opinions?

ActiveResource.destroy() should return Boolean

The destroy() method for the ActiveResource class should return a Boolean value – True if the resource was successfully deleted from the remote site or False otherwise.

This mirrors the behaviour in ActiveResource itself.

Deprecation warning due to invalid escape sequences in Python 3.7

Deprecation warnings are raised due to invalid escape sequences. This can be fixed by using raw strings or escaping the literals.

find . -iname '*.py' | grep -v example | xargs -P 4 -I{} python3.8 -Wall -m py_compile {}
./pyactiveresource/activeresource.py:18: DeprecationWarning: invalid escape sequence \w
  VALID_NAME = re.compile('[a-z_]\w*')  # Valid python attribute names

Generated URL Includes .json

Hi,

I was curious if there was a feature that would allow the default URL to be changed for a resource.

class Organization(pyactiveresource.activeresource.ActiveResource): _site = 'https://api.example.com/api/hr/' _primary_key = 'id' _headers = {"Authorization": f"Bearer {jwt}"} _primary_key = 'id'

When perform a .find() on this it's appending .json to the URL which isn't the convention for our URLs.

pyactiveresource.connection.ResourceNotFound: Not Found: https://api.example.com/api/hr/organizations/123456789.json

Need fix for SSL/TLS issue: EOF occurred in violation of protocol

Lots of posts on the urllib issue where occasional errors related to non-TLS requests crop up. This is happening within the ShopifyAPI library but obviously the connection and requests are handled here. See below for sample error stack:

File "/Library/Python/2.7/site-packages/pyactiveresource/activeresource.py", line 385, in find
    return cls._find_every(from_=from_, **kwargs)
File "/Library/Python/2.7/site-packages/pyactiveresource/activeresource.py", line 523, in _find_every
    return cls._build_list(cls.connection.get(path, cls.headers)
File "/Library/Python/2.7/site-packages/pyactiveresource/connection.py", line 329, in get
    return self.format.decode(self._open('GET', path, headers=headers).body)
File "/Library/Python/2.7/site-packages/shopify/base.py", line 23, in _open
    self.response = super(ShopifyConnection, self)._open(*args, **kwargs)
File "/Library/Python/2.7/site-packages/pyactiveresource/connection.py", line 290, in _open
  raise Error(err, url)
Error: <urlopen error [Errno 8] _ssl.c:507: EOF occurred in violation of protocol>

One reference... http://stackoverflow.com/questions/28985724/python-ssl-error-violation-of-protocols

How to Retrieve msg Attribute from a Caught ClientError Exception

Hello,

I am currently working with the pyactiveresource package and have encountered a situation where I need to extract the msg attribute from a ClientError exception. I understand that this attribute should contain detailed information about the error. However, I am unsure about the correct method to access this attribute. Here is a snippet of my code:

python
Copy code
from pyactiveresource.connection import ClientError

try:
    # Code that might throw ClientError
    ...
except ClientError as e:
    # Attempt to access the 'msg' attribute
    error_message = e.msg
    print(error_message)
    

In this code, I'm trying to catch the ClientError and then access its msg attribute. Could you please provide guidance or an example on how to properly retrieve the msg attribute from a ClientError exception in pyactiveresource?

Thank you for your assistance and for the development of this useful library.

Best regards

Problem with camelize method

There seems to be an issue with pyactiveresource.util.camelize method. I found this after experiencing an error from an order line item that had the following properties.

...
"properties": [
    {
        "name": "Message",
        "value": {
            " maxlength=": "Happy Birthday Nolan!"}
         },
    {
        "name": "Card",
        "value": "Wishing you a very Hapy Birthday.  Hope you have an awesome day!  Love, Nonno, Nonna, Lucy \u0026 Johnson"
    }
],
...

The issue stems from the camelize method being called with " maxlength" as it's argument. The last line of the traceback is:

/data/virtualenv/satellite/lib/python2.7/site-packages/pyactiveresource/util.pyc in <genexpr>((w,))
    197     """
    198     return ''.join(w[0].upper() + w[1:]
--> 199                    for w in re.sub('[^A-Z^a-z^0-9^:]+', ' ', word).split(' '))
    200
    201

IndexError: string index out of range

And here is a full traceback (run in ipython). This error seems to be related to calling the camelize method with a string with a leading or a trailing space.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.