Git Product home page Git Product logo

sdk-python's Introduction

Python SDK for CloudEvents

PyPI version

Status

This SDK is still considered a work in progress, therefore things might (and will) break with every update.

This SDK current supports the following versions of CloudEvents:

  • v1.0
  • v0.3

Python SDK

Package cloudevents provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec.

Installing

The CloudEvents SDK can be installed with pip:

pip install cloudevents

Sending CloudEvents

Below we will provide samples on how to send cloudevents using the popular requests library.

Binary HTTP CloudEvent

from cloudevents.http import CloudEvent
from cloudevents.conversion import to_binary
import requests

# Create a CloudEvent
# - The CloudEvent "id" is generated if omitted. "specversion" defaults to "1.0".
attributes = {
    "type": "com.example.sampletype1",
    "source": "https://example.com/event-producer",
}
data = {"message": "Hello World!"}
event = CloudEvent(attributes, data)

# Creates the HTTP request representation of the CloudEvent in binary content mode
headers, body = to_binary(event)

# POST
requests.post("<some-url>", data=body, headers=headers)

Structured HTTP CloudEvent

from cloudevents.conversion import to_structured
from cloudevents.http import CloudEvent
import requests

# Create a CloudEvent
# - The CloudEvent "id" is generated if omitted. "specversion" defaults to "1.0".
attributes = {
    "type": "com.example.sampletype2",
    "source": "https://example.com/event-producer",
}
data = {"message": "Hello World!"}
event = CloudEvent(attributes, data)

# Creates the HTTP request representation of the CloudEvent in structured content mode
headers, body = to_structured(event)

# POST
requests.post("<some-url>", data=body, headers=headers)

You can find a complete example of turning a CloudEvent into a HTTP request in the samples' directory.

Receiving CloudEvents

The code below shows how to consume a cloudevent using the popular python web framework flask:

from flask import Flask, request

from cloudevents.http import from_http

app = Flask(__name__)


# create an endpoint at http://localhost:/3000/
@app.route("/", methods=["POST"])
def home():
    # create a CloudEvent
    event = from_http(request.headers, request.get_data())

    # you can access cloudevent fields as seen below
    print(
        f"Found {event['id']} from {event['source']} with type "
        f"{event['type']} and specversion {event['specversion']}"
    )

    return "", 204


if __name__ == "__main__":
    app.run(port=3000)

You can find a complete example of turning a CloudEvent into a HTTP request in the samples' directory.

SDK versioning

The goal of this package is to provide support for all released versions of CloudEvents, ideally while maintaining the same API. It will use semantic versioning with following rules:

  • MAJOR version increments when backwards incompatible changes is introduced.
  • MINOR version increments when backwards compatible feature is introduced INCLUDING support for new CloudEvents version.
  • PATCH version increments when a backwards compatible bug fix is introduced.

Community

Each SDK may have its own unique processes, tooling and guidelines, common governance related material can be found in the CloudEvents docs directory. In particular, in there you will find information concerning how SDK projects are managed, guidelines for how PR reviews and approval, and our Code of Conduct information.

If there is a security concern with one of the CloudEvents specifications, or with one of the project's SDKs, please send an email to [email protected].

Additional SDK Resources

Maintenance

We use black and isort for autoformatting. We set up a tox environment to reformat the codebase.

e.g.

pip install tox
tox -e reformat

For information on releasing version bumps see RELEASING.md

sdk-python's People

Contributors

bonnevm avatar cumason123 avatar davidwmartines avatar denismakogon avatar dependabot[bot] avatar di avatar duglin avatar evankanderson avatar febus982 avatar grahamcampbell avatar grant avatar hairmare avatar jiashuchen avatar lawouach avatar pre-commit-ci[bot] avatar ryandawsonuk avatar sasha-tkachev avatar slinkydeveloper avatar xinydev avatar xsavikx 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  avatar  avatar  avatar  avatar  avatar

Watchers

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

sdk-python's Issues

Setup Release Please for Automatic Releases

Expected Behavior

Automatic releases should be setup for this repo, including publishing when pushing to master. Use tooling where possible.

Actual Behavior

Release tooling not setup. There's a release.sh file, but I'm not sure it it's automated.


Can we use GitHub Actions?

See how the Google python client uses it:

Rough steps (Github Actions):

  • Create .github/workflows folder.
  • Create .github/workflows/release-please.yml
    • Use releaseType: python and python in other places in the yaml
  • Push to master to trigger the bot.

Contact @busunkim96 from Yoshi, @grant, go/python-chat for possible help.
Read this: https://github.com/actions/setup-python, possibly setup on a personal repo

Reusing a marshaller causes failures?

I got an error with the pattern:

m = marshaller.NewDefaultHTTPMarshaller()

@app.route("/")
def handle():
    event = m.FromRequest(....)
    print(event)

Invoking this a second time, I got the following exception:

  File "env/lib/python3.5/site-packages/cloudevents/sdk/marshaller.py", line 71, in FromRequest
    content_type, self.__converters))
cloudevents.sdk.exceptions.UnsupportedEventConverter: Unable to identify valid event converter for content-type: 'No registered marshaller for application/json; charset=utf-8 in <generator object HTTPMarshaller.__init__.<locals>.<genexpr> at 0x7fbb573c6db0>'

I can look into this later this week.

CloudEvent needs __eq__ overload to be supported

Expected Behavior

event = from_http(request.get_data(), request.headers)
event1 = from_http(request.get_data(), request.headers)
assert event == event1

I would expect this to pass as we are passing in the same fields into both objects

Actual Behavior

Assertion error because cloudevent does not support equality overload

structured events don't necessarily expect application/cloudevents+json

Expected Behavior

Following crashes as CloudEvent attempts to unmarshall this as a binary event

from cloudevents.sdk.http_events import CloudEvent
import json
data = json.dumps({"id": "123", "source": "<source url>", "type": "com.sample.type", "specversion": "1.0"})
headers = {}
event = CloudEvent.from_http(data, headers)

Actual Behavior

from cloudevents.sdk.http_events import CloudEvent
import json
data = json.dumps({"id": "123", "source": "<source url>", "type": "com.sample.type", "specversion": "1.0"})
headers = {"Content-Type": "application/cloudevents+json"}
event = CloudEvent.from_http(data, headers)

We have not registered this content type with the IANA yet as seen here therefore we shouldn't expect all requests to have this header. We should instead check the http headers to determine whether event is structured or binary.

error sending event with extensions

Error at

for key, value in props.get("extensions"):

I get:

cloudevents/sdk/event/base.py", line 157, in MarshalBinary
    for key, value in props.get("extensions"):
ValueError: too many values to unpack (expected 2)

I think it should be props.get("extensions").items().

I think I'm hitting this because I'm creating one event from another and specifically passing on the extensions using SetExtensions rather adding each as an item:

            revent = (
                v02.Event().
                    SetContentType("application/json").
                    SetData(responseStr).
                    SetEventID(resp_event_id).
                    SetSource(self.model.event_source()).
                    SetEventType(self.event_type).
                    SetExtensions(event.Extensions())
            )

Improve auto-formatting tools

Expected Behavior

Some standardized auto formatting pre-commit such that developers can spend less time manually modifying code format and more time implementing logic.

Actual Behavior

Tox provides warnings but doesn't exactly auto-format. There were several complaints against tox, and developers having to create several additional commits to fix lint issues in the CI.

Possible tools

https://pypi.org/project/black/ for code formatting
https://pypi.org/project/isort/ for import path ordering
https://pypi.org/project/autoflake/ to remove unused imports
https://pypi.org/project/flake8/ for pep8 conformance

Outdated pypi package

Expected Behavior

Following code should work using the pypi package.

pip install cloudevents
python
>>> from cloudevents.sdk.http import CloudEvent

Actual Behavior

pip install cloudevents
python
>>> from cloudevents.sdk.http import CloudEvent

Throws error because module http does not exist due to pypi being outdated.

Ultimately i'm asking if everyone thinks branch v1.0.0-dev should be considered stable now, and if not what else can be done.

consolidate return types

At this moment ToRequest(event, "structured", lambda x: x) will return the data as io.StringIO, but ToRequest(event, "binary", lambda x: x) will return with respect to the returning datatype of a data_unmarshaller.

There's not suppose to be any kinds of differences. That's said, data must be returned as io.BytesIO in both cases.

Seprate http methods from event class

Need discussion on how to define the namespaces for http methods (e.g. #49 (comment)).

Expected Behavior

from cloudevents.sdk import http
http.binary.to_json(event)

Actual Behavior

from cloudevents.sdk import converters
headers, body = event.to_http(converters.TypeBinary)

The above is just an example naming schema we could use.

gh-action-pypi-publish

We may want to add the following to pypi_release.yml:

with:
    skip_existing: true

to enable readme adjustments pushed to master. Unsure if this is the best way of handling this, or if the workflow should be changed to only allowing tagged commits

V1 using extensions with dict is wrong

Using a very minimalistic event results in a non-worky cloud events:

vaikas-a01:sdk-python-master vaikas$ python3 ./app2.py
{'content-type': 'application/cloudevents+json'}
b'{"specversion": "1.0", "id": "my-id", "source": "from-vaikas", "type": "vaikas.test", "datacontenttype": "application/json", "data": "{\\"HELLO\\": \\"FROM VILLE\\"}", "extensions": {}}'

Workaround:
https://github.com/cloudevents/sdk-python/blob/master/cloudevents/sdk/event/v1.py#L31

Removing this line makes it work (but then you can't use extensions):
in v1.py

self.ce__extensions = opt.Option("extensions", dict(), False)

Repro code:

event = (
    v1.Event().
    SetContentType("application/json").
    SetEventID("my-id").
    SetSource("from-vaikas").
    SetEventType("vaikas.test").
    SetData('{"HELLO": "FROM VILLE"}')
)

m = marshaller.NewHTTPMarshaller(
    [
        structured.NewJSONHTTPCloudEventConverter()
    ]
)

#broker url

#url = 'http://localhost:8080/'

get headers and body

headers, body = m.ToRequest(event, converters.TypeStructured, lambda x: x)
print(headers)
print(body.getvalue())

#send the request
response = requests.post(url, headers=headers, data=body)

Structured extensions aren't serialized top level in to_http result

Actual Behavior

from cloudevents.sdk.http_events import CloudEvent
attributes = {"source": "<source-url>", "type": "com.issue.extensions", "example-extension": "ext1"}
data = 'Hello'
event = CloudEvent(attributes, data)
headers, body = event.to_http()
print(body)

The above code will produce the following:

b'{"specversion": "1.0", "id": "fc713795-93f6-44a8-b67e-7b8bd7071e2a", "source": "<source-url>", "type": "com.issue.extensions", "time": "2020-07-16T22:41:48.222788+00:00", "extensions": {"example-extension": "ext1"}, "data": "Hello"}'

When in fact we expectthis to be outputted:

b'{"specversion": "1.0", "id": "fc713795-93f6-44a8-b67e-7b8bd7071e2a", "source": "<source-url>", "type": "com.issue.extensions", "time": "2020-07-16T22:41:48.222788+00:00", "example-extension": "ext1", "data": "Hello"}'

Current output is readable by python CloudEvents, but possibly not by other cloudevent systems unless I'm misunderstanding extensions.

Consider a more Pythonic API

Hi all, first: thanks for providing this library. It's going to be really useful to have a standard way to work with CloudEvents from Python.

I took a preliminary look at it and my first impression was that the interface provided by this library isn't very "Pythonic", meaning that it doesn't provide an interface in a way that (I believe) would be expected by most Python developers. There are some small things (like using method names like FromRequest instead of from_request, using New in class/method names) but also larger things (like the use of method chaining, setters/getters, etc.)

I'm hoping that since this project is still in alpha, there's still time to improve the interface here (and that y'all are open to suggestions). I'll try to provide some examples below of what I'm talking about but I'm happy to clarify if anything's unclear. I'm also not intimately familiar with the CloudEvents spec, so I may have some misunderstandings as well.

Parsing upstream Event from HTTP Request

The current example for turning an HTTP request into an event:

import io

from cloudevents.sdk.event import v02
from cloudevents.sdk import marshaller

m = marshaller.NewDefaultHTTPMarshaller()
event = m.FromRequest(
    v02.Event(),
    {
        "content-type": "application/cloudevents+json",
        "ce-specversion": "0.2",
        "ce-time": "2018-10-23T12:28:22.4579346Z",
        "ce-id": "96fb5f0b-001e-0108-6dfe-da6e2806f124",
        "ce-source": "<source-url>",
        "ce-type": "word.found.name",
    },
    io.BytesIO(b"this is where your CloudEvent data"), 
    lambda x: x.read()
)
  • This makes the assumption that the user is only interested in supporting a single spec, but I think it's possible that either they would want to support multiple versions (and automatically detect which one is being used), or be able to upgrade to newer versions of the spec (and this module) without having to change their code.
  • There's only one type of marshaller provided by the library right now, HTTPMarshaller, and if it's the default maybe we should just get it by default when creating an Event, instead of making the user initialize it every time?
  • The data_unmarshaller is maybe not necessary? We're taking a bytestring, turning it into a BytesIO object to pass as data, and then using data_unmarshaller to turn it back into a bytestring. Why not just have the user do any data unmarshalling themselves before constructing the event?

Instead, consider:

from cloudevents.sdk import Event

event = Event(
    headers={
        "content-type": "application/cloudevents+json",
        "ce-specversion": "0.2",
        "ce-time": "2018-10-23T12:28:22.4579346Z",
        "ce-id": "96fb5f0b-001e-0108-6dfe-da6e2806f124",
        "ce-source": "<source-url>",
        "ce-type": "word.found.name",
    },
    body=b"this is where your CloudEvent data",
)
  • This initializes a generic Event, detects which spec it matches, and gives back a corresponding Event subclass. (If the user knew which spec they were supporting, instead of constructing an Event they could construct a v02.Event and skip the event detection)
  • The HTTPMarshaller is used by default. This also helps avoid issues like #12.
  • The data_unmarshaller field is removed (or at the very least, optional)

Creating a minimal CloudEvent

The provided example is:

from cloudevents.sdk.event import v01

event = (
    v01.Event().
    SetContentType("application/json").
    SetData('{"name":"john"}').
    SetEventID("my-id").
    SetSource("from-galaxy-far-far-away").
    SetEventTime("tomorrow").
    SetEventType("cloudevent.greet.you")
)
  • Similar to above, the user has to specify the exact event type they're expecting
  • Method chaining / fluent API: while this pattern is used by some Python libraries (pandas, various ORMs) it's not typical for the majority of Python libraries and generally not recommended.

Instead, consider the following based on the same Event class proposed above:

from cloudevents.sdk import Event

event = Event(
    data=b'{"name":"john"}',
    content_type="application/json",
    event_id="my-id",
    event_time="tomorrow",
    event_type="cloudevent.greet.you",
    source="from-galaxy-far-far-away",
)
  • The spec is automatically determined, but the user can also use a specific subclass if they prefer.
  • The constructor uses keyword arguments, so that an Event object can be created with a single method call. If the user needs to modify the event after initialization, they can act on the attributes directly (e.g. event.event_time = "yesterday").

Getting event attributes

Currently, after creating an event, if the user wanted to get one of the fields such as the event time, the user would have to do something like:

event = ...
data = event.ce__eventTime.value
  • The ce-prefix is a bit unexpected and the double-underscore is unconventional
  • Having to call .value to get the value is also unexpected

Instead, consider something like:

event = ...
data = event.event_time
  • No prefix or double underscore
  • Access the attribute value directly

Final thoughts

I realize this is proposing a pretty big overhaul of the current interface, but I think doing so would probably go a long way to lend towards the usability and maintainability of this library, and it'd be better to do it sooner than later. I'm happy to help out here, including designing/implementing these changes and helping maintain them afterwards, if it makes sense. Thanks!

from_http better exception handling

Expected Behavior

from_http(data="", headers={})

This should throw CloudEventMissingFields("Invalid data field") or similar descriptive error

Actual Behavior

First, from_http attempts to read this event as a structured event.

Because there is nothing inside data, it fails to unmarshal'. Therefore it runs json.loads("") because it expects data to be a json string with cloud event extensions.

Finally you get a json decoder error which is difficult to debug

Need to fix marshaller for None data types

Expected Behavior

from cloudevents.sdk.http import CloudEvent, to_binary_http
event = CloudEvent({"source": "<my-url>", "type": "issue.example"})
headers, body = to_binary_http(event)

Actual Behavior

Raises TypeError object of type NoneType has no len() when trying to marshall a None data type

sample not working for binary format

Hi,

I tried the sample cloudevent_to_request script and I got this:

binary CloudEvent
content-type: application/json

ce-specversion: 0.2

ce-type: cloudevent.event.type

ce-source: <event-source

ce-id: my-id

Traceback (most recent call last):
  File "./cloudevent_to_request.py", line 74, in <module>
    run_binary(event, url)
  File "./cloudevent_to_request.py", line 32, in run_binary
    print(binary_data.getvalue())
AttributeError: 'str' object has no attribute 'getvalue'

structured format worked well

Changelog doesn't reflect v02/v01 removal

Expected Behavior

Changelog should explain why versions v02 and v01 were removed

Actual Behavior

Changelog wasn't updated in that PR

Steps to Reproduce the Problem

View the changelog

Specifications

  • Platform: any

from_http bug needs exception handling/test

Expected Behavior

from cloudevents.http import from_http
from_http({"ce-id":"1", "ce-source":"s", "ce-type": "t"}, "123")

should raise MissingRequiredHeader hinting at specversion

Actual Behavior

raises int object has no attribute get because we don't check instance of raw_ce after json loading here on line 54

Might want an if statement checking the value of raw_ce, and error handlinlg appropriately

from_http doesn't accept capitalized required arguments nor None data

Expected Behavior

from cloudevents.http import from_http
headers = {
    "Ce-Source": "<my-source>",
    "Ce-Specversion": "1.0",
    "Ce-Type": "my-type",
    "Ce-Id": str(uuid4())
}
from_http("", headers)

should pass despite upper-cased attributes names.

from cloudevents.http import from_http
headers = {
    "ce-source": "<my-source>",
    "ce-specversion": "1.0",
    "ce-type": "my-type",
    "ce-id": str(uuid4())
}
from_http(None, headers)

likewise, this should return an event with data set to none.

Actual Behavior

None data causes _json_or_string to fail, Recommend addding a None if statement in that function.

Capitalized ce-specversion isn't read by from_http. Instead, from_http should make all keys lower case before passing it into future functionality.

from_http wrong exception raised on binary events with no data

Expected Behavior

from cloudevents.http import from_http
headers = {"ce-specversion": "1.0", "type": "me.type", "source": "<my-source>"}
from_http(headers, None)

I would expect the above code to raise cloudevents.exceptions.MissingRequiredFields("...")

Actual Behavior

Instead it does raise cloudevents.exceptions.InvalidStructuredJSON("...")

I believe it does this because line 48 we check whether headers is binary and if not we assume this is a structured request and try to read as json.

Perhaps instead of raising InvalidStructuredJSON we should set specversion to None and outside the if statement raise MissingRequiredFields('can't find specversion and couldn't decode json').

is_binary should check more than just ce-specversion

Expected Behavior

from cloudevents.sdk.converters.binary import NewBinaryHTTPCloudEventConverter
cnvtr = NewBinaryHTTPCloudEventConverter()
assert not cnvtr.can_read({"ce-specversion": "1.0"})

Actual Behavior

cnvtr.can_read({"ce-specversion": "1.0"})

will return true because it only checks for ce-specversion and not other required versions.

CloudEvent only uses v1

Expected Behavior

http_events.CloudEvent.from_http method currently only marshalls v1 events. This should probably be more dynamic in order to support multiple event versions. Perhaps we could restore specversion detection as seen in #36 or implement some other workaround.

Actual Behavior

Line 70 in https://github.com/cloudevents/sdk-python/blob/v1.0.0-dev/cloudevents/sdk/http_events.py

        event = marshaller.NewDefaultHTTPMarshaller().FromRequest(
            v1.Event(), headers, data, data_unmarshaller)

Automated pypi packaging

Expected Behavior

Merge v1.0.0-stable branch into master updates pypi

Actual Behavior

Must run release.sh with proper permissions.

We should setup a github action using:
https://github.com/pypa/gh-action-pypi-publish

An approximate hook in .github/workflows/main.yml could be:

- name: Publish package
  if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
  uses: pypa/gh-action-pypi-publish@master
  with:
    user: __token__
    password: ${{ secrets.pypi_password }}

This assumes we have a github secret from pypi. We need 2 things for this automated step to work:

  1. Need someone with authorization for the pypi package to set the github secret pypi_password here
  2. Need a PR adding the above job to workflows and merging it either into cloudevents/master or cloudevents/v1.0.0-dev

CloudEvent class needs better access to cloudevent fields

Expected Behavior

event = CloudEvent(headers, data)
event.specversion

Actual Behavior

event = CloudEvent(headers, data)
event['specversion']

Steps to Reproduce the Problem

from cloudevents.sdk import converters
from cloudevents.sdk.http_events import CloudEvent
import requests


attributes = {
    "Content-Type": "application/json",
    "type": "README.sample.binary",
    "id": "binary-event",
    "source": "README",
}
data = {"message": "Hello World!"}

event = CloudEvent(attributes, data)
event['specversion']

Simpler API references

Expected Behavior

from cloudevents.http import CloudEvent
Simple path to import stuff

Actual Behavior

from cloudevents.sdk.http import CloudEvent
sdk path doesn't actually hold anything therefore we would want to either sub-package http folder or possibly expose it top level by importing it within a cloudevents.__init__.py

Cloud Events AMQP Bindings.

Seems like you guys have a lot going here for HTTP, I've got a need to include the AMQP bindings for processing cloud events in Python.

I'll be trying to resolve this issue myself, is that alright?

Sample code testing

Should we unit test the sample code and if so how should we standardize this? Unsure if we should make http samples modular such that we can directly test the logic, or if tests should simply reflect at a high level what you expect CloudEvents to do.

from_http should expect headers as first parameter

Expected Behavior

def from_http(
    headers: typing.Dict[str, str],
    data: typing.Union[str, bytes],
    data_unmarshaller: types.UnmarshallerType = None,
):

This order of parameters would make cloudevents.http.from_http more consistent with the other methods which either expect some header like object first, or return headers followed by data

Actual Behavior

def from_http(
    data: typing.Union[str, bytes],
    headers: typing.Dict[str, str],
    data_unmarshaller: types.UnmarshallerType = None,
)

v1 converter doesn't check the required attributes

A json CE without required attributes can still pass the FromRequest, e.g. without id

data = { "specversion": "string", "datacontenttype": "string", "data": "Unknown Type: object,string", "data_base64": "string", "time": "2020-04-24T10:58:14.315Z", "dataschema": "string", "subject": "string", "type": "string", "source": "string" } marshaller.FromRequest( v1.Event(), dict(request.headers), io.StringIO(json.dumps(data)), lambda x: x )
meanwhile,
the v1.Event() doens't enforce the value of specversion in structured mode.
the data_base64 is put into extensions instead of treating as an optional attribute.

Missing 100% test coverage

We should add further testing to have 100% test coverage

Recommend adding the following into .tox:

    PYTESTARGS = -v -s --tb=long --cov=cloudevents --cov-report term-missing --cov-fail-under=100

CloudEvents need more descriptive errors

Expected Behavior

from_http call on an invalid request raises CloudEvent.InvalidAttributesError

Actual Behavior

from_http call on an invalid request raises ValueError which is vague

Samples additional testing

samples/http-json-cloudevents and samples/python-requests need tests. Additionally, samples/http-image-cloudevents needs to test with a flask test server

Add Travis CI Pipeline

I prefer using Travis but any CI pipeline integration with pull requests would be helpful. Is Travis the right place for this? Want me to put in a config?

is_binary and is_structured should be in http module

Expected Behavior

from cloudevents.http import is_binary, is_structured

Actual Behavior

from cloudevents.sdk.converters import is_binary, is_structured

These methods at the moment comform to the http spec but aren't generalized to other specs. Therefore we would want to move these methods into the http module

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.