Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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 from
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 → src/rpclib/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 → src/rpclib/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  src/rpclib/test/interop/test_client_http_twisted.py → ...lib/test/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 src/rpclib/test/interop/test_client_zeromq.py → src/rpclib/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/' })