Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

rpclib-2.8.0 commits #129

Merged
merged 30 commits into from

1 participant

@plq
Owner

No description provided.

@plq plq was assigned
plq added some commits
@plq plq clarify license in pypi d92fc47
@plq plq Merge branch 'master' of github.com:plq/rpclib ef1ae10
@plq plq fix service tests that were failing due to null transport in Application d441600
@plq plq now using the new AuxMethodContext class to pass various to the aux m…
…ethods.
4a2e734
@plq plq fix json protocol deserialization 21101a2
@plq plq rpclib.protocol.json: log the error on getattr if there's any a46b202
@plq plq add docstring to wsgimounter 679a178
@plq plq rpclib.aux: initialize_context function initializes ctx.aux. 610e329
@plq plq negligible 77c4328
@plq plq nullserver got prettier. a17d807
@plq plq rpclib.util.xml.get_object_as_xml: pass no_namespace=True to strip na…
…mespaces out of the xml output.
19683f0
@plq plq rpclib.aux: remove debug statements 3983a10
@plq plq changelog update 58fe120
@plq plq rpclib.aux.thread: Initialize threadpool as late as possible to let t…
…he process fork before spawning threads.
5c17662
@plq plq replace tabs with 4 spaces / remove trailing space aaa8c06
@plq plq make httprpc test standalone. 1e88498
@plq plq reorganize and fix tests, make interop tests mostly standalone.
still issues with running them with a single py.test command though.
e3cf052
@plq plq remove dead code. 1a90a9a
@plq plq fix zeromq server caccff1
@plq plq make optional dependencies really optional 9e155e4
@plq plq negligible 6e584e4
@plq plq fix typo. whoops. d771dff
@plq plq make SelfReference work again. c47195a
@plq plq ignore packages whose names start with _ in automatic namespace assig…
…nment
5bb1052
@plq plq get_object_as_xml and get_xml_as_object argument change.
now get_object_as_xml can also get class suggestion.
ab91a3e
@plq plq examples/helloworld_soap_twisted: fiddled with default namespace pref…
…ix. xmlns:tns="..." is now xmlns="..."
584d4f4
@plq plq add docstring to InterfaceDocumentBase 5e31435
@plq plq make _body_style='bare' work. At least in Soap11 05c9dee
@plq plq pep8 tweak 1d4795c
@plq plq don't put alias in interface.classes set a08a3de
@plq
Owner

okay, this goes in. further fixes will be made in following pull requests.

@plq plq merged commit a08a3de into arskom:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 8, 2012
  1. @plq

    clarify license in pypi

    plq authored
  2. @plq
Commits on May 9, 2012
  1. @plq
  2. @plq
  3. @plq

    fix json protocol deserialization

    plq authored
  4. @plq
  5. @plq

    add docstring to wsgimounter

    plq authored
  6. @plq
  7. @plq

    negligible

    plq authored
  8. @plq

    nullserver got prettier.

    plq authored
  9. @plq

    rpclib.util.xml.get_object_as_xml: pass no_namespace=True to strip na…

    plq authored
    …mespaces out of the xml output.
Commits on May 10, 2012
  1. @plq
  2. @plq

    changelog update

    plq authored
  3. @plq

    rpclib.aux.thread: Initialize threadpool as late as possible to let t…

    plq authored
    …he process fork before spawning threads.
Commits on May 12, 2012
  1. @plq
  2. @plq

    make httprpc test standalone.

    plq authored
  3. @plq

    reorganize and fix tests, make interop tests mostly standalone.

    plq authored
    still issues with running them with a single py.test command though.
  4. @plq

    remove dead code.

    plq authored
  5. @plq

    fix zeromq server

    plq authored
  6. @plq
  7. @plq

    negligible

    plq authored
  8. @plq

    fix typo. whoops.

    plq authored
Commits on May 14, 2012
  1. @plq

    make SelfReference work again.

    plq authored
Commits on May 15, 2012
  1. @plq
Commits on May 17, 2012
  1. @plq

    get_object_as_xml and get_xml_as_object argument change.

    plq authored
    now get_object_as_xml can also get class suggestion.
Commits on May 18, 2012
  1. @plq

    examples/helloworld_soap_twisted: fiddled with default namespace pref…

    plq authored
    …ix. xmlns:tns="..." is now xmlns="..."
  2. @plq
  3. @plq
  4. @plq

    pep8 tweak

    plq authored
  5. @plq
This page is out of date. Refresh to see the latest.
Showing with 466 additions and 341 deletions.
  1. +3 −1 CHANGELOG.rst
  2. +1 −1  doc/source/manual/highlevel.rst
  3. +1 −1  doc/source/manual/t_and_p.rst
  4. +124 −132 doc/source/manual/validation.rst
  5. +1 −1  examples/authentication/http_cookie/server_soap.py
  6. +5 −0 examples/helloworld_soap_twisted.py
  7. +1 −1  examples/validation.py
  8. +2 −2 setup.py
  9. +1 −0  src/rpclib/__init__.py
  10. +14 −0 src/rpclib/_base.py
  11. +3 −2 src/rpclib/application.py
  12. +1 −1  src/rpclib/aux/__init__.py
  13. +15 −7 src/rpclib/aux/_base.py
  14. +2 −2 src/rpclib/aux/sync.py
  15. +7 −3 src/rpclib/aux/thread.py
  16. +2 −1  src/rpclib/client/_base.py
  17. +16 −2 src/rpclib/const/ansi_color.py
  18. +8 −1 src/rpclib/interface/_base.py
  19. +5 −5 src/rpclib/interface/wsdl/wsdl11.py
  20. +5 −2 src/rpclib/interface/xml_schema/_base.py
  21. +1 −5 src/rpclib/interface/xml_schema/model/complex.py
  22. +9 −2 src/rpclib/model/_base.py
  23. +20 −12 src/rpclib/model/complex.py
  24. +6 −2 src/rpclib/model/primitive.py
  25. +3 −2 src/rpclib/protocol/_base.py
  26. +1 −1  src/rpclib/protocol/html.py
  27. +1 −1  src/rpclib/protocol/http.py
  28. +3 −3 src/rpclib/protocol/json/__init__.py
  29. +12 −9 src/rpclib/protocol/xml/_base.py
  30. +6 −0 src/rpclib/protocol/xml/model/complex.py
  31. +33 −12 src/rpclib/server/null.py
  32. +1 −1  src/rpclib/server/twisted/__init__.py
  33. +4 −1 src/rpclib/server/wsgi.py
  34. +2 −2 src/rpclib/server/zeromq.py
  35. +42 −0 src/rpclib/test/interop/{_test_client_base.py → _test_soap_client_base.py}
  36. +4 −1 src/rpclib/test/interop/server/httprpc_pod_basic.py
  37. +3 −1 src/rpclib/test/interop/server/soap_http_basic.py
  38. +8 −2 src/rpclib/test/interop/server/soap_zeromq.py
  39. +19 −0 src/rpclib/test/interop/test_httprpc.py
  40. +3 −1 src/rpclib/test/interop/{test_client_http.py → test_soap_client_http.py}
  41. +3 −0  src/rpclib/test/interop/{test_client_http_twisted.py → test_soap_client_http_twisted.py}
  42. +4 −1 src/rpclib/test/interop/{test_client_zeromq.py → test_soap_client_zeromq.py}
  43. +5 −23 src/rpclib/test/interop/test_suds.py
  44. +3 −3 src/rpclib/test/test_service.py
  45. +1 −1  src/rpclib/test/wsdl/__init__.py
  46. +28 −28 src/rpclib/test/wsdl/test_bindings.py
  47. +0 −58 src/rpclib/util/__init__.py
  48. +5 −0 src/rpclib/util/wsgi_wrapper.py
  49. +19 −4 src/rpclib/util/xml.py
View
4 CHANGELOG.rst
@@ -18,6 +18,8 @@ rpclib-2.8.0-beta
now ignored.
* Add support for async methods, which execute after the primary user code
returns. Currently, the only async execution method is via threads.
+ * Xml & friends: Start tags are now in the same namespace as the definitions
+ themselves. Intermediate tags are in the parent's namespace, just as before.
* Full change log:
* https://github.com/arskom/rpclib/pull/128
* https://github.com/arskom/rpclib/pull/129
@@ -77,7 +79,7 @@ rpclib-2.5.1-beta
rpclib-2.5.0-beta
-----------------
- * Implemented fanout support for transports and protocols that can handle
+ * Implemented fanout support for transports and protocols that can handle
that.
* Implemented a helper module that generates a Soap/Wsdl 1.1 application in
``rpclib.util.simple``
View
2  doc/source/manual/highlevel.rst
@@ -73,7 +73,7 @@ procedure calls to the outside world. The following is used to wrap that
code:
* **User Methods**:
- User methods are the code that you wrote and decided to use rpclib to
+ User methods are the code that you wrote and decided to use rpclib to
expose to the outside world.
* **Decorators**:
View
2  doc/source/manual/t_and_p.rst
@@ -100,7 +100,7 @@ is stored. Workers are identified by an integer. ::
nullable=False)
The consumer is a :class:`rpclib.server._base.ServerBase` child that receives
-requests by polling the database.
+requests by polling the database.
The transport is for displaying it in the Wsdl. While it's irrelevant here, it's
nice to put it in: ::
View
256 doc/source/manual/validation.rst
@@ -2,7 +2,7 @@
Input Validation
================
-This is necessary in the cases in which you have to ensure that the received
+This is necessary in the cases in which you have to ensure that the received
data comply with a given format, such as:
- a number must be within a certain range
@@ -13,21 +13,22 @@ data comply with a given format, such as:
Data validation can be handled by two subsystems:
XML schema
- such rules are enforced by *lxml*, the underlying XML parsing library
+ such rules are enforced by *lxml*, the underlying XML parsing library
+
"Soft" level
- *rpclib* itself can apply additional checks after the data were validated by
- the layer underneath
+ *rpclib* itself can apply additional checks after the data were validated by
+ the layer underneath
The differences between them are:
-- Soft validation ignores unknown fields, while *lxml* validation rejects
+- Soft validation ignores unknown fields, while *lxml* validation rejects
them.
-- Soft validation doesn't care about namespaces, while *lxml* validation
+- Soft validation doesn't care about namespaces, while *lxml* validation
rejects unexpected namespaces.
- Soft validation works with any transport protocol supported by *rpclib*,
while *lxml* validation only works for XML data (i.e. just SOAP/XML).
-============================== ======== =========
+============================== ======== =========
Criteria lxml soft
============================== ======== =========
Unknown fields reject ignore
@@ -35,32 +36,29 @@ Unknown namespaces reject ignore
Supported transport protocols SOAP/XML any
============================== ======== =========
+.. NOTE::
+ The two validation sybsystems operate independently, you can use either one,
+ but not both at the same time. The validator is indicated when instantiating
+ the protocol: ``validator='soft'`` or ``validator='lxml'``.
+
+ ::
+
+ #using 'soft' validation with HttpRpc
+ application = Application([NameOfMonthService],
+ tns='rpclib.examples.multiprot',
+ in_protocol=HttpRpc(validator='soft'),
+ out_protocol=HttpRpc()
+ )
+
+ #using lxml validation with Soap
+ application = Application([UserService],
+ tns='rpclib.examples.authentication',
+ interface=Wsdl11(),
+ in_protocol=Soap11(validator='lxml'),
+ out_protocol=Soap11()
+ )
-
-.. NOTE::
- The two validation sybsystems operate independently, you can use either one,
- but not both at the same time. The validator is indicated when instantiating
- the protocol: ``validator='soft'`` or ``validator='lxml'``.
-
- ::
-
- #using 'soft' validation with HttpRpc
- application = Application([NameOfMonthService],
- tns='rpclib.examples.multiprot',
- in_protocol=HttpRpc(validator='soft'),
- out_protocol=HttpRpc()
- )
-
- #using lxml validation with Soap
- application = Application([UserService],
- tns='rpclib.examples.authentication',
- interface=Wsdl11(),
- in_protocol=Soap11(validator='lxml'),
- out_protocol=Soap11()
- )
-
-
Simple validation at the XML schema level
@@ -69,11 +67,11 @@ This applies to all the primitive data types, and is suitable for simple logical
conditions.
.. NOTE::
- Constraints applied at this level are reflected in the XML schema itself,
- thus a client that retrieves the WSDL of the service will be able to see
- what the constraints are.
- As it was mentioned in the introduction, such validation is only effective
- in the context of SOAP/XML.
+ Constraints applied at this level are reflected in the XML schema itself,
+ thus a client that retrieves the WSDL of the service will be able to see
+ what the constraints are.
+ As it was mentioned in the introduction, such validation is only effective
+ in the context of SOAP/XML.
Any primitive type
@@ -83,60 +81,60 @@ along with their default values
- ``default = None`` - default value if the input is ``None``
- ``nillable = True`` - if True, the item is optional
-- ``min_occurs = 0`` - set this to 1 to make the type mandatory. Can be set to
+- ``min_occurs = 0`` - set this to 1 to make the type mandatory. Can be set to
any positive integer
-- ``max_occurs = 1`` - can be set to any strictly positive integer. Values
+- ``max_occurs = 1`` - can be set to any strictly positive integer. Values
greater than 1 will imply an iterable of objects as native Python type. Can be
set to ``unbounded`` for arbitrary number of arguments
-
+
.. NOTE::
- As of rpclib-2.8.0, use ``float('inf')`` instead of ``unbounded``.
-
+ As of rpclib-2.8.0, use ``float('inf')`` instead of ``unbounded``.
+
These rules can be combined, the example below illustrates how to create a
mandatory string:
- String(min_occurs=1, min_len=1, nillable=False)
-
+ String(min_occurs=1, min_len=1, nillable=False)
+
Numbers
~~~~~~~
-Integers and other countable numerical data types (i.e. except Float or
-Double) can be compared with specific values, using the following keywords:
+Integers and other countable numerical data types (i.e. except Float or
+Double) can be compared with specific values, using the following keywords:
``ge``, ``gt``, ``le``, ``lt`` (they correspond to >=, >, <=, <) ::
- Integer(ge=1, le=12) #an integer between 1 and 12, i.e. 1 <= x <= 12
- Integer(gt=1, le=42) #1 < x <= 42
-
+ Integer(ge=1, le=12) #an integer between 1 and 12, i.e. 1 <= x <= 12
+ Integer(gt=1, le=42) #1 < x <= 42
+
Strings
~~~~~~~
These can be validated against a regular expression: ::
- String(pattern = "[0-9]+") #must contain at least one digit, digits only
-
-
+ String(pattern = "[0-9]+") #must contain at least one digit, digits only
+
+
Length checks can be enforced as well: ::
- String(min_len = 5, max_len = 10)
- String(max_len = 10) #implicit value for min_len = 0
+ String(min_len = 5, max_len = 10)
+ String(max_len = 10) #implicit value for min_len = 0
Other string-related constraints are related to encoding issues. You can specify
- which encoding the strings must be in
- how to handle the situations in which a string cannot be decoded properly (to
- understand how this works, consult `Python's documentation
+ understand how this works, consult `Python's documentation
<http://docs.python.org/howto/unicode.html>`_ ::
String(encoding = 'win-1251')
String(unicode_errors = 'strict') #could be 'replace' or 'ignore'
-
+
These restrictions can be combined: ::
- String(encoding = 'win-1251', max_len = 20)
- String(min_len = 5, max_len = 20, pattern = '[a-z]')
-
+ String(encoding = 'win-1251', max_len = 20)
+ String(min_len = 5, max_len = 20, pattern = '[a-z]')
+
Possible values
~~~~~~~~~~~~~~~
@@ -144,9 +142,9 @@ Sometimes you may want to allow only a certain set of values, which would be
difficult to describe in terms of an interval. If this is the case, you can
explicitly indicate the set: ::
- Integer(values = [1984, 13, 45, 42])
- Unicode(values = [u"alpha", u"bravo", u"charlie"]) #note the 'u' prefix
-
+ Integer(values = [1984, 13, 45, 42])
+ Unicode(values = [u"alpha", u"bravo", u"charlie"]) #note the 'u' prefix
+
Extending the rules of XML validation
@@ -159,7 +157,7 @@ from ``ModelBase``.
After that, you must apply the relevant changes in the code that generates the
-XML schema, otherwise these attributes will **not** be visible in the output.
+XML schema, otherwise these attributes will **not** be visible in the output.
Examples of how to do that:
https://github.com/arskom/rpclib/tree/master/src/rpclib/interface/xml_schema/model
@@ -170,7 +168,7 @@ https://github.com/arskom/rpclib/tree/master/src/rpclib/interface/xml_schema/mod
Advanced validation
-------------------
-*rpclib* offers several primitives for this purpose, they are defined in
+*rpclib* offers several primitives for this purpose, they are defined in
the **ModelBase** class, from which all the types are derived:
https://github.com/arskom/rpclib/blob/master/src/rpclib/model/_base.py
@@ -181,36 +179,36 @@ These primitives are:
- *validate_native* - invoked after the string is converted to a specific Python
value.
-Since XML is a text file, when you read it - you get a string; thus
-*validate_string* is the first filter that can be applied to such data.
+Since XML is a text file, when you read it - you get a string; thus
+*validate_string* is the first filter that can be applied to such data.
At a later stage, the data can be converted to something else, for example - a
number. Once that conversion occurs, you can apply some additional checks - this
is handled by *validate_native*.
- >>> stringNumber = '123'
- >>> stringNumber
- '123' #note the quotes, it is a string
- >>> number = int(stringNumber)
- >>> number
- 123 #notice the absence of quotes, it is a number
- >>> stringNumber == 123
- False #note quite what one would expect, right?
- >>> number == 123
- True
-
-In the example above, *number* is an actual number and can be validated with
-*validate_native*, whereas *stringNumber* is a string and can be validated by
+ >>> stringNumber = '123'
+ >>> stringNumber
+ '123' #note the quotes, it is a string
+ >>> number = int(stringNumber)
+ >>> number
+ 123 #notice the absence of quotes, it is a number
+ >>> stringNumber == 123
+ False #note quite what one would expect, right?
+ >>> number == 123
+ True
+
+In the example above, *number* is an actual number and can be validated with
+*validate_native*, whereas *stringNumber* is a string and can be validated by
*validate_string*.
-Another case in which you need a native validation would be a sanity check on a
-date. Imagine that you have to verify if a received date complies with the
-*"YYYY-MM-DDThh:mm:ss"* pattern (which is *xs:datetime*). You can devise a
+Another case in which you need a native validation would be a sanity check on a
+date. Imagine that you have to verify if a received date complies with the
+*"YYYY-MM-DDThh:mm:ss"* pattern (which is *xs:datetime*). You can devise a
regular expression that will look for 4 digits (YYYY), followed by a dash, then
by 2 more digits for the month, etc. But such a regexp will happily absorb dates
that have "13" as a month number, even though that doesn't make sense. You can
-make a more complex regexp to deal with that, but it will be very hard to
+make a more complex regexp to deal with that, but it will be very hard to
maintain and debug. The best approach is to convert the string into a datetime
object and then perform all the checks you want.
@@ -224,16 +222,16 @@ We'll have to declare our own class, derived from *Unicode* (which, in turn, is
derived from *SimpleModel*, which inherits from *ModelBase*).::
- class SpecialString(Unicode):
- """Custom string type that prohibis the use of colons"""
-
- @staticmethod
- def validate_string(cls, value):
- """Override the function to enforce our own verification logic"""
- if value:
- if ':' in value:
- return True
- return False
+ class SpecialString(Unicode):
+ """Custom string type that prohibis the use of colons"""
+
+ @staticmethod
+ def validate_string(cls, value):
+ """Override the function to enforce our own verification logic"""
+ if value:
+ if ':' in value:
+ return True
+ return False
@@ -245,62 +243,56 @@ This time both flavours of validation are combined: *validate_string* to see if
it is a number, and then *validate_native* to see if it is prime.
.. NOTE::
- *rpclib* has a primitive type called *Integer*, it is reasonable to use that
- one as a basis for this custom type. *Unicode* is used in this example
- simply because it is an opportunity to show both types of validation
- functions in action. This may be a good academic example, but it is
- certainly not the approach one would use in production code.
-
+ *rpclib* has a primitive type called *Integer*, it is reasonable to use that
+ one as a basis for this custom type. *Unicode* is used in this example
+ simply because it is an opportunity to show both types of validation
+ functions in action. This may be a good academic example, but it is
+ certainly not the approach one would use in production code.
::
- class PrimeNumber(Unicode):
- """Custom integer type that only works with prime numbers"""
-
- @staticmethod
- def validate_string(cls, value):
- """See if it is a number"""
- import re
-
- if re.search("[0-9]+", value):
- return True
- else:
- return False
-
- @staticmethod
- def validate_native(cls, value):
- """See if it is prime"""
-
- #calling a hypothetical function that checks if it is prime
- return IsPrime(value)
+ class PrimeNumber(Unicode):
+ """Custom integer type that only works with prime numbers"""
+ @staticmethod
+ def validate_string(cls, value):
+ """See if it is a number"""
+ import re
+
+ if re.search("[0-9]+", value):
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def validate_native(cls, value):
+ """See if it is prime"""
+
+ #calling a hypothetical function that checks if it is prime
+ return IsPrime(value)
.. NOTE::
- Constraints applied at this level do **not modify** the XML schema itself,
- thus a client that retrieves the WSDL of the service will not be aware of
- these restrictions. Keep this in mind and make sure that validation rules
- that are not visible in the XML schema are documented elsewhere.
-
-.. NOTE::
- When overriding ``validate_string`` or ``validate_native`` in a custom type
- class, the validation functions from the parent class are **not invoked**.
- If you wish to apply those validation functions as well, you must call them
- explicitly.
+ Constraints applied at this level do **not modify** the XML schema itself,
+ thus a client that retrieves the WSDL of the service will not be aware of
+ these restrictions. Keep this in mind and make sure that validation rules
+ that are not visible in the XML schema are documented elsewhere.
+.. NOTE::
+ When overriding ``validate_string`` or ``validate_native`` in a custom type
+ class, the validation functions from the parent class are **not invoked**.
+ If you wish to apply those validation functions as well, you must call them
+ explicitly.
-
Summary
=======
- simple checks can be applied at the XML schema level, you can control:
-
- the length of a string
- the pattern with which a string must comply
- a numeric interval, etc
-
-- *rpclib* can apply arbitrary rules for the validation of input data
+- *rpclib* can apply arbitrary rules for the validation of input data
- *validate_string* is the first applied filter
- *validate_native* is the applied at the second phase
- Override these functions in your derived class to add new validation rules
- The validation functions must return a *boolean* value
- - These rules are **not** shown in the XML schema
+ - These rules are **not** shown in the XML schema
View
2  examples/authentication/http_cookie/server_soap.py
@@ -176,7 +176,7 @@ def _on_method_call(ctx):
if not session_id in session_db:
raise AuthenticationError(session_id[0])
ctx.udc = session_id[0] # user name
-
+
UserService.event_manager.add_listener('method_call', _on_method_call)
View
5 examples/helloworld_soap_twisted.py
@@ -62,6 +62,11 @@ def say_hello(name, times):
if __name__=='__main__':
application = Application([HelloWorldService], 'rpclib.examples.hello.twisted',
interface=Wsdl11(), in_protocol=Soap11(), out_protocol=Soap11())
+
+ application.interface.nsmap[None] = application.interface.nsmap['tns']
+ application.interface.prefmap[application.interface.nsmap['tns']] = None
+ del application.interface.nsmap['tns']
+
wsgi_app = WsgiApplication(application)
print('listening on 0.0.0.0:7789')
View
2  examples/validation.py
@@ -24,7 +24,7 @@ def get_name_of_month(month):
from rpclib.server.wsgi import WsgiApplication
from wsgiref.simple_server import make_server
-host = '127.0.0.1'
+host = '127.0.0.1'
port = 9912
server = make_server(host, port, WsgiApplication(rest))
View
4 setup.py
@@ -17,7 +17,7 @@
tools for creating and publishing web services in python. This package
features on-demand wsdl generation for the published services, a
wsgi-compliant web application, support for complex class structures, binary
-attachments, and a simple framework for creating additional serialization
+attachments, and a simple framework for creating additional serialization
mechanisms.
This project uses lxml as it's XML API, providing full namespace support.
@@ -61,7 +61,7 @@
maintainer='Burak Arslan',
maintainer_email='burak+rpclib@arskom.com.tr',
url='http://github.com/arskom/rpclib',
- license='LGPL',
+ license='LGPL-2.1',
zip_safe=False,
install_requires=[
'pytz',
View
1  src/rpclib/__init__.py
@@ -19,6 +19,7 @@
__version__ = '2.8.0-beta'
+from rpclib._base import AuxMethodContext
from rpclib._base import TransportContext
from rpclib._base import EventContext
from rpclib._base import MethodContext
View
14 src/rpclib/_base.py
@@ -27,6 +27,15 @@
from rpclib.const.xml_ns import DEFAULT_NS
from rpclib.util.oset import oset
+class AuxMethodContext(object):
+ """Generic object that holds information specific to auxiliary methods"""
+ def __init__(self, p_ctx, error):
+ self.p_ctx = p_ctx
+ """Primary context that this method was bound to."""
+
+ self.error = error
+ """Error from primary context (if any)."""
+
class TransportContext(object):
"""Generic object that holds transport-specific context information"""
def __init__(self, transport, type=None):
@@ -88,6 +97,11 @@ def __init__(self, transport):
events, as you'd probably want to separate the event data from the
method data."""
+ self.aux = None
+ """Auxiliary-method specific context. You can use this to share data
+ between auxiliary sessions. This is not set in primary methods.
+ """
+
self.method_request_string = None
"""This is used as a basis on deciding which native method to call."""
View
5 src/rpclib/application.py
@@ -145,7 +145,8 @@ def process_request(self, ctx):
def call_wrapper(self, ctx):
"""This method calls the call_wrapper method in the service definition.
This can be overridden to make an application-wide custom exception
- management."""
+ management.
+ """
return ctx.service_class.call_wrapper(ctx)
@@ -159,5 +160,5 @@ def reinitialize(self):
aux_memo = set()
for s,d in self.interface.method_id_map.values():
if d.aux is not None and not id(d.aux) in aux_memo:
- d.aux.initialize(server, db=self.main)
+ d.aux.initialize(server)
aux_memo.add(id(d.aux))
View
2  src/rpclib/aux/__init__.py
@@ -20,7 +20,7 @@
"""Module that contains backends that process the auxiliary contexts. These
result from non-primary functions in service definitions.
-Classes from this package will have the ``AuxProc`` suffix, short for
+Classes from this package will have the ``AuxProc`` suffix, short for
"Auxiliary Processor". Sounds neat, huh? :)
This package is EXPERIMENTAL. Stay away from it.
View
22 src/rpclib/aux/_base.py
@@ -20,11 +20,14 @@
import logging
logger = logging.getLogger(__name__)
+from rpclib import AuxMethodContext
+
def process_contexts(server, contexts, p_ctx, error=None):
for ctx in contexts:
+ ctx.descriptor.aux.initialize_context(ctx, p_ctx, error)
if error is None or ctx.descriptor.aux.process_exceptions:
- ctx.descriptor.aux.process_context(server, ctx, p_ctx, error)
+ ctx.descriptor.aux.process_context(server, ctx)
class AuxProcBase(object):
@@ -32,11 +35,7 @@ def __init__(self, process_exceptions=False):
self.methods = []
self.process_exceptions = process_exceptions
- def initialize(self):
- pass
-
- def process(self, server, ctx, p_ctx, p_error, *args, **kwargs):
- logger.debug("Executing %r" % ctx.service_class.get_method_id(ctx.descriptor))
+ def process(self, server, ctx, *args, **kwargs):
server.get_in_object(ctx)
if ctx.in_error is not None:
logger.exception(ctx.in_error)
@@ -49,4 +48,13 @@ def process(self, server, ctx, p_ctx, p_error, *args, **kwargs):
server.get_out_string(ctx)
for s in ctx.out_string:
- logger.debug(s)
+ pass
+
+ def process_context(self, server, ctx, p_ctx, p_error):
+ raise NotImplementedError()
+
+ def initialize(self, server):
+ pass
+
+ def initialize_context(self, ctx, p_ctx, error):
+ ctx.aux = AuxMethodContext(p_ctx, error)
View
4 src/rpclib/aux/sync.py
@@ -24,5 +24,5 @@
class SyncAuxProc(AuxProcBase):
- def process_context(self, server, ctx, p_ctx, p_error):
- self.process(server, ctx, p_ctx, p_error)
+ def process_context(self, server, ctx):
+ self.process(server, ctx)
View
10 src/rpclib/aux/thread.py
@@ -28,15 +28,19 @@
class ThreadAuxProc(AuxProcBase):
def __init__(self, pool_size=1):
AuxProcBase.__init__(self)
+
+ self.pool = None
self.__pool_size = pool_size
- self.pool = ThreadPool(pool_size)
@property
def pool_size(self):
return self.__pool_size
- def process_context(self, server, ctx, p_ctx, p_error, *args, **kwargs):
- a = [server, ctx, p_ctx, p_error]
+ def process_context(self, server, ctx, *args, **kwargs):
+ a = [server, ctx]
a.extend(args)
self.pool.apply_async(self.process, a, kwargs)
+
+ def initialize(self, server):
+ self.pool = ThreadPool(self.__pool_size)
View
3  src/rpclib/client/_base.py
@@ -149,7 +149,8 @@ def get_in_object(self, ctx):
type_info = ctx.descriptor.out_message._type_info
- if len(ctx.descriptor.out_message._type_info) == 1: # TODO: Non-Wrapped Object Support
+ # TODO: Non-Wrapped Object Support
+ if len(ctx.descriptor.out_message._type_info) == 1:
wrapper_attribute = type_info.keys()[0]
ctx.in_object = getattr(ctx.in_object, wrapper_attribute, None)
View
18 src/rpclib/const/ansi_color.py
@@ -1,14 +1,22 @@
+DARK_RED = ""
LIGHT_GREEN = ""
LIGHT_RED = ""
+LIGHT_BLUE = ""
END_COLOR = ""
def enable_color():
global LIGHT_GREEN
- LIGHT_GREEN = "\033[92m"
+ LIGHT_GREEN = "\033[1;32m"
global LIGHT_RED
- LIGHT_RED = "\033[91m"
+ LIGHT_RED = "\033[1;31m"
+
+ global LIGHT_BLUE
+ LIGHT_BLUE = "\033[1;34m"
+
+ global DARK_RED
+ DARK_RED = "\033[0;31m"
global END_COLOR
END_COLOR = "\033[0m"
@@ -21,6 +29,12 @@ def disable_color():
global LIGHT_RED
LIGHT_RED = ""
+ global LIGHT_BLUE
+ LIGHT_BLUE = ""
+
+ global DARK_RED
+ DARK_RED = ""
+
global END_COLOR
END_COLOR = ""
View
9 src/rpclib/interface/_base.py
@@ -32,6 +32,7 @@
from rpclib.const.suffix import RESPONSE_SUFFIX
from rpclib.model import ModelBase
+from rpclib.model.complex import Alias
from rpclib.model.complex import ComplexModelBase
@@ -276,6 +277,11 @@ def get_namespace_prefix(self, ns):
return pref
def add_class(self, cls):
+ if issubclass(cls, Alias):
+ if issubclass(cls._target, ComplexModelBase):
+ self.add_class(cls._target)
+ return
+
if self.has_class(cls):
return
@@ -323,6 +329,8 @@ def is_valid_import(self, ns):
class InterfaceDocumentBase(object):
+ """Base class for all interface document implementations"""
+
def __init__(self, interface):
self.interface = interface
@@ -335,7 +343,6 @@ def build_interface_document(self, cls):
raise NotImplementedError('Extend and override.')
-
def get_interface_document(self, cls):
"""This function is called by server transports that try to satisfy the
request for the interface document. This should just return a previously
View
10 src/rpclib/interface/wsdl/wsdl11.py
@@ -405,7 +405,7 @@ def add_bindings_for_methods(self, service, root, service_name,
cb_binding):
pref_tns = self.interface.get_namespace_prefix(service.get_tns())
-
+
def inner(binding):
for method in service.public_methods.values():
operation = etree.Element('{%s}operation' % _ns_wsdl)
@@ -508,11 +508,11 @@ def inner(binding):
mid_header.set('use', 'literal')
binding.append(operation)
-
+
port_type_list = service.get_port_types()
if len(port_type_list) > 0:
for port_type_name in port_type_list:
-
+
# create binding nodes
binding = etree.SubElement(root, '{%s}binding' % _ns_wsdl)
binding.set('name', port_type_name)
@@ -520,7 +520,7 @@ def inner(binding):
transport = etree.SubElement(binding, '{%s}binding' % _ns_soap)
transport.set('style', 'document')
-
+
inner(binding)
else:
@@ -533,7 +533,7 @@ def inner(binding):
transport = etree.SubElement(cb_binding, '{%s}binding' % _ns_soap)
transport.set('style', 'document')
transport.set('transport', self.interface.app.transport)
-
+
inner(cb_binding)
return cb_binding
View
7 src/rpclib/interface/xml_schema/_base.py
@@ -37,11 +37,13 @@
from rpclib.model.primitive import Decimal
from rpclib.model.primitive import String
from rpclib.model.complex import ComplexModelBase
+from rpclib.model.complex import Alias
from rpclib.model.enum import EnumBase
from rpclib.model.fault import Fault
from rpclib.util.odict import odict
from rpclib.interface.xml_schema.model import simple_add
+from rpclib.interface.xml_schema.model.complex import alias_add
from rpclib.interface.xml_schema.model.complex import complex_add
from rpclib.interface.xml_schema.model.fault import fault_add
from rpclib.interface.xml_schema.model.enum import enum_add
@@ -52,6 +54,7 @@
_add_handlers = cdict({
object: lambda interface, cls: None,
+ Alias: alias_add,
SimpleModel: simple_add,
ComplexModelBase: complex_add,
Fault: fault_add,
@@ -161,7 +164,7 @@ def build_validation_schema(self):
def get_schema_node(self, pref):
"""Return schema node for the given namespace prefix."""
- # create schema node
+
if not (pref in self.schema_dict):
schema = etree.Element("{%s}schema" % _ns_xsd, nsmap=self.interface.nsmap)
@@ -171,7 +174,7 @@ def get_schema_node(self, pref):
self.schema_dict[pref] = schema
else:
- schema = self.schema_nodes[pref]
+ schema = self.schema_dict[pref]
return schema
View
6 src/rpclib/interface/xml_schema/model/complex.py
@@ -122,13 +122,9 @@ def complex_add(document, cls):
document.add_element(cls, element)
-
def alias_add(document, cls):
- if not document.has_class(cls._target):
- document.add(cls._target)
-
element = etree.Element('{%s}element' % namespace.xsd)
element.set('name', cls.get_type_name())
- element.set('type', cls._target.get_type_name_ns(document.interface.app))
+ element.set('type', cls._target.get_type_name_ns(document.interface))
document.add_element(cls, element)
View
11 src/rpclib/model/_base.py
@@ -146,8 +146,15 @@ def resolve_namespace(cls, default_ns):
cls.__namespace__ = default_ns
if cls.__namespace__ is None:
- cls.__namespace__ = cls.__module__
-
+ ret = []
+ for f in cls.__module__.split('.'):
+ if f.startswith('_'):
+ break
+ else:
+ ret.append(f)
+
+ cls.__namespace__ = '.'.join(ret)
+
@classmethod
def get_type_name(cls):
"""Returns the class name unless the __type_name__ attribute is defined.
View
32 src/rpclib/model/complex.py
@@ -87,7 +87,8 @@ def describe(self, name, element, app):
class SelfReference(object):
- pass
+ def __init__(self):
+ raise NotImplementedError()
class ComplexModelMeta(type(ModelBase)):
@@ -140,12 +141,17 @@ def __new__(cls, cls_name, cls_bases, cls_dict):
% (cls_name, k))
else:
_type_info = cls_dict['_type_info']
+
if not isinstance(_type_info, TypeInfo):
cls_dict['_type_info'] = TypeInfo(_type_info)
for k, v in _type_info.items():
- if not issubclass(v, ModelBase):
+ if issubclass(v, SelfReference):
+ pass
+
+ elif not issubclass(v, ModelBase):
raise ValueError( (k,v) )
+
elif issubclass(v, Array) and len(v._type_info) != 1:
raise Exception("Invalid Array definition in %s.%s."
% (cls_name, k))
@@ -153,10 +159,10 @@ def __new__(cls, cls_name, cls_bases, cls_dict):
return type(ModelBase).__new__(cls, cls_name, cls_bases, cls_dict)
def __init__(self, cls_name, cls_bases, cls_dict):
- for k in cls_dict:
- if cls_dict[k] is SelfReference:
- cls_dict[k] = self
- self._type_info[k] = self
+ type_info = cls_dict['_type_info']
+ for k in type_info:
+ if type_info[k] is SelfReference:
+ type_info[k] = self
type(ModelBase).__init__(self, cls_name, cls_bases, cls_dict)
@@ -169,8 +175,8 @@ class ComplexModelBase(ModelBase):
def __init__(self, **kwargs):
super(ComplexModelBase, self).__init__()
- # this ugliness is due to sqlalchemy's forcing relevant types for database
- # fields
+ # this ugliness is due to sqlalchemy's forcing of relevant types for
+ # database fields
for k in self.get_flat_type_info(self.__class__).keys():
try:
delattr(self, k)
@@ -257,7 +263,6 @@ def get_members_pairs(cls, inst):
else:
yield (k, [v.to_string(subvalue)])
-
@classmethod
@nillable_dict
def to_dict(cls, value):
@@ -338,7 +343,7 @@ def from_string(cls, string):
def resolve_namespace(cls, default_ns):
if getattr(cls, '__extends__', None) != None:
cls.__extends__.resolve_namespace(cls.__extends__, default_ns)
-
+
ModelBase.resolve_namespace(cls, default_ns)
for k, v in cls._type_info.items():
@@ -378,10 +383,13 @@ def alias(type_name, namespace, target):
cls_dict['__namespace__'] = namespace
cls_dict['__type_name__'] = type_name
- cls_dict['_type_info'] = getattr(target, '_type_info', ())
cls_dict['_target'] = target
- return ComplexModelMeta(type_name, (ClassAlias,), cls_dict)
+ ti = getattr(target, '_type_info', None)
+ if ti is not None:
+ cls_dict['_type_info'] = ti
+
+ return ComplexModelMeta(type_name, (Alias,), cls_dict)
@classmethod
def customize(cls, **kwargs):
View
8 src/rpclib/model/primitive.py
@@ -34,7 +34,6 @@
from collections import deque
-from lxml import etree
from pytz import FixedOffset
from rpclib.model import SimpleModel
@@ -42,6 +41,11 @@
from rpclib.error import ValidationError
from rpclib.error import Fault
+try:
+ from lxml import etree
+except ImportError:
+ pass
+
string_encoding = 'utf8'
_date_pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
@@ -469,7 +473,7 @@ class DateTime(SimpleModel):
class Attributes(SimpleModel.Attributes):
"""Customizable attributes of the :class:`rpclib.model.primitive.DateTime`
type."""
-
+
format = None
"""DateTime format fed to the ``strftime`` function. See:
http://docs.python.org/library/datetime.html?highlight=strftime#strftime-strptime-behavior"""
View
5 src/rpclib/protocol/_base.py
@@ -74,7 +74,7 @@ class ProtocolBase(object):
classes can implement only the required subset of the public methods.
The ProtocolBase class supports the following events:
-
+
* ``before_deserialize``:
Called before the deserialization operation is attempted.
@@ -188,7 +188,8 @@ def set_method_descriptor(self, ctx):
def generate_method_contexts(self, ctx):
call_handles = self.get_call_handles(ctx)
if len(call_handles) == 0:
- raise ResourceNotFoundError('Method %r not found.' % ctx.method_request_string)
+ raise ResourceNotFoundError('Method %r not found.' %
+ ctx.method_request_string)
retval = []
for sc, d in call_handles:
View
2  src/rpclib/protocol/html.py
@@ -111,7 +111,7 @@ def serialize(self, ctx, message):
setattr(result_message, attr_name, ctx.out_object[i])
ctx.out_header_doc = None
- ctx.out_body_doc = self.serialize_impl(result_message_class,
+ ctx.out_body_doc = self.serialize_impl(result_message_class,
result_message)
ctx.out_document = ctx.out_body_doc
View
2  src/rpclib/protocol/http.py
@@ -89,7 +89,7 @@ def set_validator(self, validator):
def create_in_document(self, ctx, in_string_encoding=None):
assert ctx.transport.type.endswith('http'), \
- ("This protocol only works with an http transport, not: %s, (in %r)"
+ ("This protocol only works with an http transport, not: %s, (in %r)"
% (ctx.transport.type, ctx.transport))
ctx.in_document = ctx.transport.req
View
6 src/rpclib/protocol/json/__init__.py
@@ -88,7 +88,7 @@ def create_in_document(self, ctx, in_string_encoding=None):
except JSONDecodeError, e:
raise Fault('Client.JsonDecodeError', repr(e))
- def decompose_incoming_envelope(self, ctx):
+ def decompose_incoming_envelope(self, ctx, message):
"""Sets ``ctx.in_body_doc``, ``ctx.in_header_doc`` and
``ctx.method_request_string`` using ``ctx.in_document``.
"""
@@ -96,7 +96,6 @@ def decompose_incoming_envelope(self, ctx):
# set ctx.in_header
ctx.transport.in_header_doc = None # use an rpc protocol if you want headers.
- # set ctx.in_body
doc = ctx.in_document
ctx.in_header_doc = None
@@ -290,7 +289,8 @@ def get_member_pairs(self, cls, inst):
for k, v in cls._type_info.items():
try:
sub_value = getattr(inst, k, None)
- except: # to guard against e.g. sqlalchemy throwing NoSuchColumnError
+ except Exception, e: # to guard against e.g. sqlalchemy throwing NoSuchColumnError
+ logger.error("Error getting %r: %r" %(k,e))
sub_value = None
if v.Attributes.max_occurs > 1:
View
21 src/rpclib/protocol/xml/_base.py
@@ -32,6 +32,7 @@
from rpclib.model import ModelBase
from rpclib.model.binary import ByteArray
from rpclib.model.binary import Attachment
+from rpclib.model.complex import Alias
from rpclib.model.complex import Array
from rpclib.model.complex import Iterable
from rpclib.model.complex import ComplexModelBase
@@ -44,6 +45,7 @@
from rpclib.protocol.xml.model import base_to_parent_element
from rpclib.protocol.xml.model.binary import binary_to_parent_element
+from rpclib.protocol.xml.model.complex import alias_to_parent_element
from rpclib.protocol.xml.model.complex import complex_to_parent_element
from rpclib.protocol.xml.model.enum import enum_to_parent_element
from rpclib.protocol.xml.model.fault import fault_to_parent_element
@@ -89,28 +91,29 @@ def __init__(self, app=None, validator=None, xml_declaration=True,
self.cleanup_namespaces = cleanup_namespaces
self.serialization_handlers = cdict({
+ AnyXml: xml_to_parent_element,
+ Alias: alias_to_parent_element,
+ Fault: fault_to_parent_element,
+ AnyDict: dict_to_parent_element,
+ EnumBase: enum_to_parent_element,
ModelBase: base_to_parent_element,
ByteArray: binary_to_parent_element,
Attachment: binary_to_parent_element,
ComplexModelBase: complex_to_parent_element,
- Fault: fault_to_parent_element,
- AnyXml: xml_to_parent_element,
- AnyDict: dict_to_parent_element,
- EnumBase: enum_to_parent_element,
})
self.deserialization_handlers = cdict({
+ AnyXml: xml_from_element,
+ Fault: fault_from_element,
+ AnyDict: dict_from_element,
+ EnumBase: enum_from_element,
ModelBase: base_from_element,
ByteArray: binary_from_element,
Attachment: binary_from_element,
ComplexModelBase: complex_from_element,
- Fault: fault_from_element,
- AnyXml: xml_from_element,
- AnyDict: dict_from_element,
- EnumBase: enum_from_element,
- Array: array_from_element,
Iterable: iterable_from_element,
+ Array: array_from_element,
})
self.log_messages = (logger.level == logging.DEBUG)
View
6 src/rpclib/protocol/xml/model/complex.py
@@ -63,6 +63,12 @@ def complex_to_parent_element(prot, cls, value, tns, parent_elt, name=None):
get_members_etree(prot, cls, inst, element)
+@nillable_value
+def alias_to_parent_element(prot, cls, value, tns, parent_elt, name=None):
+ if name is None:
+ name = cls.get_type_name()
+
+ prot.to_parent_element(cls._target, value._target, tns, parent_elt, name)
@nillable_element
def complex_from_element(prot, cls, element):
View
45 src/rpclib/server/null.py
@@ -22,12 +22,19 @@
import logging
logger = logging.getLogger(__name__)
+from rpclib import MethodContext
+from rpclib import AuxMethodContext
from rpclib.client._base import Factory
from rpclib.const.ansi_color import LIGHT_RED
+from rpclib.const.ansi_color import LIGHT_BLUE
from rpclib.const.ansi_color import END_COLOR
-from rpclib._base import MethodContext
from rpclib.server import ServerBase
+_big_header = ('=' * 40) + LIGHT_RED
+_big_footer = END_COLOR + ('=' * 40)
+_small_header = ('-' * 20) + LIGHT_BLUE
+_small_footer = END_COLOR + ('-' * 20)
+
class NullServer(ServerBase):
"""A server that doesn't support any transport at all -- it's implemented
to test services without having to run a server.
@@ -47,7 +54,7 @@ def get_wsdl(self):
return self.app.get_interface_document(self.url)
def set_options(self, **kwargs):
- self.service.in_header = kwargs.get('soapheaders', None)
+ self.service.in_header = kwargs.get('soapheaders', self.service.in_header)
class _FunctionProxy(object):
def __init__(self, server, app):
@@ -74,25 +81,33 @@ def __call__(self, *args, **kwargs):
initial_ctx.transport.type = NullServer.transport
contexts = self.app.in_protocol.generate_method_contexts(initial_ctx)
+
+ cnt = 0
+ retval = None
+ logger.warning( "%s start request %s" % (_big_header, _big_footer) )
+
for ctx in contexts:
- # set logging.getLogger('rpclib.server.null').setLevel(logging.CRITICAL)
+ if cnt == 0:
+ p_ctx = ctx
+ else:
+ ctx.descriptor.aux.initialize_context(ctx, p_ctx, error=None)
+
+ # do logging.getLogger('rpclib.server.null').setLevel(logging.CRITICAL)
# to hide the following
- _header = ('=' * 30) + LIGHT_RED
- _footer = END_COLOR + ('=' * 30)
- logger.warning( "%s start request %s" % (_header, _footer) )
+ logger.warning( "%s start context %s" % (_small_header, _small_footer) )
self.app.process_request(ctx)
- logger.warning( "%s end request %s" % (_header, _footer) )
+ logger.warning( "%s end context %s" % (_small_header, _small_footer) )
if ctx.out_error:
raise ctx.out_error
else:
if len(ctx.descriptor.out_message._type_info) == 0:
- retval = None
+ _retval = None
elif len(ctx.descriptor.out_message._type_info) == 1:
- retval = ctx.out_object[0]
+ _retval = ctx.out_object[0]
# workaround to have the context disposed of when the caller is
# done with the return value. the context is sometimes needed to
@@ -100,19 +115,25 @@ def __call__(self, *args, **kwargs):
# sqlalchemy object bound to a session that's defined in the
# context object).
try:
- retval.__ctx__ = ctx
+ _retval.__ctx__ = ctx
except AttributeError:
# not all objects let this happen. (eg. built-in types
# like str, but they don't need the context anyway).
pass
else:
- retval = ctx.out_object
+ _retval = ctx.out_object
# same as above
try:
- retval[0].__ctx__ = ctx
+ _retval[0].__ctx__ = ctx
except AttributeError:
pass
+ if cnt == 0:
+ retval = _retval
+ cnt += 1
+
+ logger.warning( "%s end request %s" % (_big_header, _big_footer) )
+
return retval
View
2  src/rpclib/server/twisted/__init__.py
@@ -215,7 +215,7 @@ def __handle_wsdl_request(self, request):
request.setHeader(k,v)
return ctx.transport.wsdl
-
+
except Exception, e:
ctx.transport.wsdl_error = e
self.http_transport.event_manager.fire_event('wsdl_exception', ctx)
View
5 src/rpclib/server/wsgi.py
@@ -34,7 +34,10 @@
except ImportError: # Python 3
from urllib.parse import parse_qs
-from werkzeug.formparser import parse_form_data
+try:
+ from werkzeug.formparser import parse_form_data
+except ImportError:
+ pass
from rpclib.server.http import HttpMethodContext
from rpclib.server.http import HttpTransportContext
View
4 src/rpclib/server/zeromq.py
@@ -57,7 +57,7 @@ def serve_forever(self):
while True:
error = None
- initial_ctx = ZmqMethodContext(self.app)
+ initial_ctx = ZmqMethodContext(self)
initial_ctx.in_string = [self.zmq_socket.recv()]
contexts = self.generate_contexts(initial_ctx)
@@ -65,7 +65,7 @@ def serve_forever(self):
if p_ctx.in_error:
p_ctx.out_object = p_ctx.in_error
error = p_ctx.in_error
-
+
else:
self.get_in_object(p_ctx)
View
42 src/rpclib/test/interop/_test_client_base.py → ...ib/test/interop/_test_soap_client_base.py
@@ -17,13 +17,55 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
#
+import time
import unittest
from rpclib.model.fault import Fault
from datetime import datetime
+import socket
+
+server_started = {}
+
+def test_port_open(port):
+ host = '127.0.0.1'
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((host, port))
+ s.shutdown(2)
+
+ return True
+
+def run_server(server_type):
+ if server_type == 'http':
+ from rpclib.test.interop.server.soap_http_basic import main
+ from rpclib.test.interop.server.soap_http_basic import PORT
+
+ elif server_type == 'zeromq':
+ from rpclib.test.interop.server.soap_zeromq import main
+ from rpclib.test.interop.server.soap_zeromq import PORT
+
+ else:
+ raise ValueError(server_type)
+
+ if server_started.get(PORT, None) is None:
+ def run_server():
+ main()
+
+ import thread
+ thread.start_new_thread(run_server, ())
+
+ # FIXME: Does anybody have a better idea?
+ time.sleep(2)
+
+ server_started[PORT] = test_port_open(PORT)
+
+
class RpclibClientTestBase(object):
+ def setUp(self, server_type):
+ run_server(server_type)
+
def test_echo_boolean(self):
val = True
ret = self.client.service.echo_boolean(val)
View
5 src/rpclib/test/interop/server/httprpc_pod_basic.py
@@ -33,7 +33,7 @@
httprpc_soap_application = Application(services,
'rpclib.test.interop.server.httprpc.pod', HttpRpc(), HttpRpc(), Wsdl11())
-if __name__ == '__main__':
+def main():
try:
from wsgiref.simple_server import make_server
from wsgiref.validate import validator
@@ -47,3 +47,6 @@
except ImportError:
print("Error: example server code requires Python >= 2.5")
+
+if __name__ == '__main__':
+ main()
View
4 src/rpclib/test/interop/server/soap_http_basic.py
@@ -31,13 +31,15 @@
soap_application = Application(services, 'rpclib.test.interop.server',
Soap11(validator='lxml', cleanup_namespaces=True), Soap11(), Wsdl11())
+PORT = 9753
+
def main():
try:
from wsgiref.simple_server import make_server
from wsgiref.validate import validator
wsgi_application = WsgiApplication(soap_application)
- server = make_server('0.0.0.0', 9753, validator(wsgi_application))
+ server = make_server('127.0.0.1', PORT, validator(wsgi_application))
logger.info('Starting interop server at %s:%s.' % ('0.0.0.0', 9753))
logger.info('WSDL is at: /?wsdl')
View
10 src/rpclib/test/interop/server/soap_zeromq.py
@@ -23,8 +23,11 @@
from rpclib.server.zeromq import ZeroMQServer
-if __name__ == '__main__':
- url = "tcp://*:5555"
+PORT = 5555
+
+def main():
+ url = "tcp://127.0.0.1:%d" % PORT
+
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('rpclib.protocol.xml').setLevel(logging.DEBUG)
@@ -37,3 +40,6 @@
logging.info("************************")
server.serve_forever()
+
+if __name__ == '__main__':
+ main()
View
19 src/rpclib/test/interop/test_httprpc.py
@@ -17,6 +17,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
#
+import time
import pytz
import unittest
@@ -31,7 +32,25 @@
from datetime import datetime
+_server_started = False
+
class TestHttpRpc(unittest.TestCase):
+ def setUp(self):
+ global _server_started
+
+ if not _server_started:
+ def run_server():
+ from rpclib.test.interop.server.httprpc_pod_basic import main
+ main()
+
+ import thread
+ thread.start_new_thread(run_server, ())
+
+ # FIXME: Does anybody have a better idea?
+ time.sleep(2)
+
+ _server_started = True
+
def test_404(self):
url = 'http://localhost:9757/404'
try:
View
4 src/rpclib/test/interop/test_client_http.py → ...lib/test/interop/test_soap_client_http.py
@@ -20,12 +20,14 @@
import unittest
from rpclib.client.http import HttpClient
-from rpclib.test.interop._test_client_base import RpclibClientTestBase
+from rpclib.test.interop._test_soap_client_base import RpclibClientTestBase
from rpclib.test.interop.server.soap_http_basic import soap_application
from rpclib.util.etreeconv import root_dict_to_etree
class TestRpclibHttpClient(RpclibClientTestBase, unittest.TestCase):
def setUp(self):
+ RpclibClientTestBase.setUp(self, 'http')
+
self.client = HttpClient('http://localhost:9753/', soap_application)
self.ns = "rpclib.test.interop.server._service"
View
3  .../test/interop/test_client_http_twisted.py → .../interop/test_soap_client_http_twisted.py
@@ -18,12 +18,15 @@
#
from twisted.trial import unittest
+from rpclib.test.interop._test_soap_client_base import run_server
from rpclib.client.twisted import TwistedHttpClient
from rpclib.test.interop.server.soap_http_basic import soap_application
class TestRpclibHttpClient(unittest.TestCase):
def setUp(self):
+ run_server('http')
+
self.ns = "rpclib.test.interop.server._service"
self.client = TwistedHttpClient('http://localhost:9753/', soap_application)
View
5 ...rpclib/test/interop/test_client_zeromq.py → ...b/test/interop/test_soap_client_zeromq.py
@@ -19,12 +19,15 @@
import unittest
-from rpclib.test.interop._test_client_base import RpclibClientTestBase
+from rpclib.test.interop._test_soap_client_base import RpclibClientTestBase
from rpclib.client.zeromq import ZeroMQClient
from rpclib.test.interop.server.soap_http_basic import soap_application
+
class TestRpclibZmqClient(RpclibClientTestBase, unittest.TestCase):
def setUp(self):
+ RpclibClientTestBase.setUp(self, 'zeromq')
+
self.client = ZeroMQClient('tcp://localhost:5555', soap_application)
self.ns = "rpclib.test.interop.server._service"
View
28 src/rpclib/test/interop/test_suds.py
@@ -18,40 +18,22 @@
#
import unittest
-import time
-import urllib2
from suds.client import Client
from suds import WebFault
from datetime import datetime
+from rpclib.test.interop._test_soap_client_base import RpclibClientTestBase
+
import logging
suds_logger = logging.getLogger('suds')
suds_logger.setLevel(logging.INFO)
-_server_started = False
-
-class TestSuds(unittest.TestCase):
+class TestSuds(RpclibClientTestBase, unittest.TestCase):
def setUp(self):
- global _server_started
-
- if not _server_started:
- def run_server():
- from rpclib.test.interop.server.soap_http_basic import main
- main()
-
- import thread
- thread.start_new_thread(run_server, ())
-
- _server_started = True
-
- while True:
- try:
- self.client = Client("http://localhost:9753/?wsdl", cache=None)
- break
- except urllib2.URLError:
- time.sleep(1)
+ RpclibClientTestBase.setUp(self, 'http')
+ self.client = Client("http://localhost:9753/?wsdl", cache=None)
self.ns = "rpclib.test.interop.server._service"
def test_echo_simple_boolean_array(self):
View
6 src/rpclib/test/test_service.py
@@ -130,6 +130,7 @@ def multi(ctx, s):
class TestSingle(unittest.TestCase):
def setUp(self):
self.app = Application([TestService], 'tns', Soap11(), Soap11())
+ self.app.transport = 'null.rpclib'
self.srv = TestService()
wsdl = Wsdl11(self.app.interface)
@@ -143,14 +144,13 @@ def test_portypes(self):
len(self.srv.public_methods), len(porttype.getchildren()))
def test_override_param_names(self):
- # FIXME: This test must be rewritten.
-
for n in ['self', 'import', 'return', 'from']:
- self.assertTrue(n in self.wsdl_str, '"%s" not in self.wsdl_str' % n)
+ assert n in self.wsdl_str, '"%s" not in self.wsdl_str'
class TestMultiple(unittest.TestCase):
def setUp(self):
self.app = Application([MultipleReturnService], 'tns', Soap11(), Soap11())
+ self.app.transport = 'none'
self.wsdl = Wsdl11(self.app.interface)
self.wsdl.build_interface_document('URL')
View
2  src/rpclib/test/wsdl/__init__.py
@@ -23,7 +23,7 @@
import rpclib.const.xml_ns as ns
def build_app(service_list, tns, name):
- app = Application(service_list, tns, Wsdl11(),
+ app = Application(service_list, tns, Wsdl11(),
Soap11(), Soap11(), name=name)
app.transport = 'http://schemas.xmlsoap.org/soap/http'
return app
View
56 src/rpclib/test/wsdl/test_bindings.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+#encoding: utf8
#
# rpclib - Copyright (C) rpclib contributors.
#
@@ -43,24 +44,24 @@ def test_binding_simple(self):
sa.interface.build_interface_document(self.url)
services = sa.interface.root_elt.xpath(
- '/wsdl:definitions/wsdl:service',
- namespaces = {
+ '/wsdl:definitions/wsdl:service',
+ namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
self.assertEqual(len(services), 1)
-
+
portTypes = sa.interface.root_elt.xpath(
- '/wsdl:definitions/wsdl:portType',
- namespaces = {
+ '/wsdl:definitions/wsdl:portType',
+ namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
self.assertEqual(len(portTypes), 1)
ports = sa.interface.root_elt.xpath(
- '/wsdl:definitions/wsdl:service[@name="%s"]/wsdl:port' %
- "S1",
+ '/wsdl:definitions/wsdl:service[@name="%s"]/wsdl:port' %
+ "S1",
namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
self.assertEqual(len(ports), 1)
-
+
def test_binding_multiple(self):
sa = build_app(
@@ -70,60 +71,59 @@ def test_binding_multiple(self):
)
sa.interface.build_interface_document(self.url)
- # 2 Service,
+ # 2 Service,
# First has 1 port
# Second has 2
-
+
# => need 2 service, 3 port and 3 bindings
services = sa.interface.root_elt.xpath(
- '/wsdl:definitions/wsdl:service',
- namespaces = {
+ '/wsdl:definitions/wsdl:service',
+ namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
self.assertEqual(len(services), 2)
-
-
+
+
portTypes = sa.interface.root_elt.xpath(
- '/wsdl:definitions/wsdl:portType',
- namespaces = {
+ '/wsdl:definitions/wsdl:portType',
+ namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
self.assertEqual(len(portTypes), 3)
bindings = sa.interface.root_elt.xpath(
- '/wsdl:definitions/wsdl:binding',
+ '/wsdl:definitions/wsdl:binding',
namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
self.assertEqual(len(bindings), 3)
ports = sa.interface.root_elt.xpath(
- '/wsdl:definitions/wsdl:service[@name="%s"]/wsdl:port' %
- SinglePortService.__service_name__,
+ '/wsdl:definitions/wsdl:service[@name="%s"]/wsdl:port' %
+ SinglePortService.__service_name__,
namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
self.assertEqual(len(ports), 1)
-
+
ports = sa.interface.root_elt.xpath(
- '/wsdl:definitions/wsdl:service[@name="%s"]/wsdl:port' %
- "DoublePortService",
+ '/wsdl:definitions/wsdl:service[@name="%s"]/wsdl:port' %
+ "DoublePortService",
namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
self.assertEqual(len(ports), 2)
-
+
# checking name and type
#service SinglePortService
for srv in ( SinglePortService, DoublePortService ):
for port in srv.__port_types__:
bindings = sa.interface.root_elt.xpath(
'/wsdl:definitions/wsdl:binding[@name="%s"]' %
- port,
+ port,
namespaces = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/' })
- self.assertEqual(bindings[0].get('type'), "tns:%s" % port)
+ self.assertEqual(bindings[0].get('type'), "tns:%s" % port)
+
+
-
-
-
View
58 src/rpclib/util/__init__.py
@@ -29,64 +29,6 @@
from urllib.parse import splithost
from urllib.parse import quote
-from rpclib.const import xml_ns as ns
-
-from lxml import etree
-
-def create_relates_to_header(relatesTo, attrs={}):
- '''Creates a 'relatesTo' header for async callbacks'''
- relatesToElement = etree.Element(
- '{%s}RelatesTo' % ns.wsa)
- for k, v in attrs.items():
- relatesToElement.set(k, v)
- relatesToElement.text = relatesTo
- return relatesToElement
-
-
-def create_callback_info_headers(message_id, reply_to):
- '''Creates MessageId and ReplyTo headers for initiating an
- async function'''
- message_id = etree.Element('{%s}MessageID' % ns.wsa)
- message_id.text = message_id
-
- reply_to = etree.Element('{%s}ReplyTo' % ns.wsa)
- address = etree.SubElement(reply_to, '{%s}Address' % ns.wsa)
- address.text = reply_to
-
- return message_id, reply_to
-
-def get_callback_info(request):
- '''
- Retrieves the messageId and replyToAddress from the message header.