Skip to content

Commit dd6da27

Browse files
author
Dariusz Suchojad
committed
SESPRINGPYTHONPY-132: Synching branch with trunk.
git-svn-id: https://src.springframework.org/svn/se-springpython-py/sandbox/dsuch/jira/SESPRINGPYTHONPY-132@758 ce8fead1-4192-4296-8608-a705134b927f
2 parents ff398d4 + cf15a89 commit dd6da27

15 files changed

+1512
-146
lines changed

springpython/docs/reference/src/objects.xml

Lines changed: 335 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -737,8 +737,121 @@ service = container.get_object("MovieLister")
737737
</objects>
738738
]]></programlisting>
739739

740-
</section>
740+
<section id="objects-config-xmlconfig-inheritance">
741+
<title>Object definition inheritance</title>
742+
743+
<para>
744+
XMLConfig's definitions may be stacked up into hierarchies of abstract
745+
parents and their children objects. A child object not
746+
only inherits all the properties and constructor arguments from
747+
its parent but it can also easily override any of the inherited
748+
values. This can save a lot of typing when configuring
749+
non-trivial application contexts which would otherwise need to
750+
repeat the same configuration properties over many objects
751+
definitions.
752+
</para>
753+
<para>
754+
An abstract object is identified by having an
755+
<emphasis>abstract="True"</emphasis> attribute and the child
756+
ones are those which have a <emphasis>parent</emphasis> attribute
757+
set to ID of an object from which the properties or constructor
758+
arguments should be inherited. Child objects must not specify
759+
the <emphasis>class</emphasis> attribute, its value is taken
760+
from their parents.
761+
</para>
762+
<para>
763+
An object may be both a child and an abstract one.
764+
</para>
765+
766+
<para>
767+
Here's a hypothetical configuration of a set of services exposed
768+
by a server. Note how you can easily change the CRM environment
769+
you're invoking by merely changing the concrete service's
770+
(get_customer_id or get_customer_profile) parent ID.
771+
</para>
772+
773+
<programlisting><![CDATA[
774+
<?xml version="1.0" encoding="UTF-8"?>
775+
<objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1"
776+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
777+
xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1
778+
http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd">
779+
780+
<object id="service" class="springpythontest.support.testSupportClasses.Service" scope="singleton" abstract="True" lazy-init="True">
781+
<property name="ip"><value>192.168.1.153</value></property>
782+
</object>
783+
784+
<object id="crm_service_dev" parent="service" abstract="True">
785+
<property name="port"><value>3392</value></property>
786+
</object>
787+
788+
<object id="crm_service_test" parent="service" abstract="True">
789+
<property name="port"><value>3393</value></property>
790+
</object>
791+
792+
<object id="get_customer_id" parent="crm_service_dev">
793+
<property name="path"><value>/soap/invoke/get_customer_id</value></property>
794+
</object>
795+
796+
<object id="get_customer_profile" parent="crm_service_test">
797+
<property name="path"><value>/soap/invoke/get_customer_profile</value></property>
798+
</object>
799+
800+
</objects>
801+
]]></programlisting>
802+
803+
<para>
804+
Here's how you can override inherited properties; both get_customer_id
805+
and get_customer_profile object definitions will inherit the
806+
<emphasis>path</emphasis> property however the actual objects returned by
807+
the container will use local, overridden, values of the property.
808+
</para>
809+
810+
<programlisting><![CDATA[
811+
<?xml version="1.0" encoding="UTF-8"?>
812+
<objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1"
813+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
814+
xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1
815+
http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd">
816+
817+
<object id="service" class="springpythontest.support.testSupportClasses.Service" scope="singleton" abstract="True" lazy-init="True">
818+
<property name="ip"><value>192.168.1.153</value></property>
819+
<property name="port"><value>3392</value></property>
820+
<property name="path"><value>/DOES-NOT-EXIST</value></property>
821+
</object>
822+
823+
<object id="get_customer_id" parent="service">
824+
<property name="path"><value>/soap/invoke/get_customer_id</value></property>
825+
</object>
826+
827+
<object id="get_customer_profile" parent="service">
828+
<property name="path"><value>/soap/invoke/get_customer_profile</value></property>
829+
</object>
830+
831+
</objects>
832+
]]></programlisting>
833+
834+
<para>
835+
If you need to get an abstract object from a container, use the .get_object's <emphasis>ignore_abstract</emphasis> parameter,
836+
otherwise <emphasis>springpython.container.AbstractObjectException</emphasis> will be raised. Observe the difference:
837+
</para>
838+
839+
<programlisting><![CDATA[
840+
# .. skip creating the context
841+
842+
# No exception will be raised, even though 'service' is an abstract object
843+
service = ctx.get_object("service", ignore_abstract=True)
844+
845+
# Will show the object
846+
print service
741847
848+
# Will raise AbstractObjectException
849+
service = ctx.get_object("service")
850+
]]></programlisting>
851+
</section>
852+
853+
</section>
854+
742855
<section id="objects-config-yamlconfig">
743856
<title><classname>YamlConfig</classname> - Spring Python's YAML format</title>
744857

@@ -1199,6 +1312,129 @@ constructor-args:
11991312

12001313
</section>
12011314

1315+
<section id="objects-config-yamlconfig-inheritance">
1316+
<title>Object definition inheritance</title>
1317+
1318+
<para>
1319+
Just like XMLConfig, YamlConfig allows for wiring the objects
1320+
definitions into hierarchies of abstract and children objects,
1321+
thus this section is in most parts a repetition of what's
1322+
documented
1323+
<link linkend="objects-config-xmlconfig-inheritance">here</link>
1324+
</para>
1325+
1326+
<para>
1327+
Definitions may be stacked up into hierarchies of abstract
1328+
parents and their children objects. A child object not
1329+
only inherits all the properties and constructor arguments from
1330+
its parent but it can also easily override any of the inherited
1331+
values. This can save a lot of typing when configuring
1332+
non-trivial application contexts which would otherwise need to
1333+
repeat the same configuration properties over many objects
1334+
definitions.
1335+
</para>
1336+
<para>
1337+
An abstract object is identified by having an
1338+
<emphasis>abstract</emphasis> attribute equal to True and the child
1339+
ones are those which have a <emphasis>parent</emphasis> attribute
1340+
set to ID of an object from which the properties or constructor
1341+
arguments should be inherited. Child objects must not specify
1342+
the <emphasis>class</emphasis> attribute, its value is taken
1343+
from their parents.
1344+
</para>
1345+
<para>
1346+
An object may be both a child and an abstract one.
1347+
</para>
1348+
1349+
<para>
1350+
Here's a hypothetical configuration of a set of services exposed
1351+
by a server. Note how you can easily change the CRM environment
1352+
you're invoking by merely changing the concrete service's
1353+
(get_customer_id or get_customer_profile) parent ID.
1354+
</para>
1355+
1356+
<programlisting><![CDATA[
1357+
objects:
1358+
- object: service
1359+
class: springpythontest.support.testSupportClasses.Service
1360+
abstract: True
1361+
scope: singleton
1362+
lazy-init: True
1363+
properties:
1364+
ip: 192.168.1.153
1365+
1366+
- object: crm_service_dev
1367+
abstract: True
1368+
parent: service
1369+
properties:
1370+
port: "3392"
1371+
1372+
- object: crm_service_test
1373+
abstract: True
1374+
parent: service
1375+
properties:
1376+
port: "3393"
1377+
1378+
- object: get_customer_id
1379+
parent: crm_service_dev
1380+
properties:
1381+
path: /soap/invoke/get_customer_id
1382+
1383+
- object: get_customer_profile
1384+
parent: crm_service_test
1385+
properties:
1386+
path: /soap/invoke/get_customer_profile
1387+
]]></programlisting>
1388+
1389+
<para>
1390+
Here's how you can override inherited properties; both get_customer_id
1391+
and get_customer_profile object definitions will inherit the
1392+
<emphasis>path</emphasis> property however the actual objects returned by
1393+
the container will use local, overridden, values of the property.
1394+
</para>
1395+
1396+
<programlisting><![CDATA[
1397+
objects:
1398+
- object: service
1399+
class: foo.Service
1400+
abstract: True
1401+
scope: singleton
1402+
lazy-init: True
1403+
properties:
1404+
ip: 192.168.1.153
1405+
port: "3392"
1406+
path: /DOES-NOT-EXIST
1407+
1408+
- object: get_customer_id
1409+
parent: service
1410+
properties:
1411+
path: /soap/invoke/get_customer_id
1412+
1413+
- object: get_customer_profile
1414+
parent: service
1415+
properties:
1416+
path: /soap/invoke/get_customer_profile
1417+
]]></programlisting>
1418+
1419+
<para>
1420+
If you need to get an abstract object from a container, use the .get_object's <emphasis>ignore_abstract</emphasis> parameter,
1421+
otherwise <emphasis>springpython.container.AbstractObjectException</emphasis> will be raised. Observe the difference:
1422+
</para>
1423+
1424+
<programlisting><![CDATA[
1425+
# .. skip creating the context
1426+
1427+
# No exception will be raised, even though 'service' is an abstract object
1428+
service = ctx.get_object("service", ignore_abstract=True)
1429+
1430+
# Will show the object
1431+
print service
1432+
1433+
# Will raise AbstractObjectException
1434+
service = ctx.get_object("service")
1435+
]]></programlisting>
1436+
</section>
1437+
12021438
</section>
12031439

12041440
<section id="objects-config-object">
@@ -1250,6 +1486,104 @@ from springpython.context import ApplicationContext
12501486
12511487
container = ApplicationContext(MovieBasedApplicationContext())
12521488
service = container.get_object("MovieLister")
1489+
]]></programlisting>
1490+
1491+
<para>PythonConfig's support for abstract objects is different to
1492+
that of
1493+
<link linkend="objects-config-xmlconfig-inheritance">XMLConfig</link>
1494+
or
1495+
<link linkend="objects-config-yamlconfig-inheritance">YamlConfig</link>.
1496+
With PythonConfig, the children object do not automatically
1497+
inherit nor override the parents' properties, they in fact
1498+
<emphasis>receive</emphasis> the values returned by their
1499+
parents and it's up to them to decide, using Python code,
1500+
whether to use or to discard the values received.
1501+
</para>
1502+
1503+
<para>
1504+
A child object must have as many optional arguments as there
1505+
are expected to be returned by its parent.
1506+
</para>
1507+
1508+
<para>
1509+
Observe that in the following example the child definitions must
1510+
define an optional 'req' argument; in runtime they will be passed
1511+
its value basing on what their parent object will return.
1512+
Note also that <emphasis>request</emphasis> is of PROTOTYPE
1513+
scope, if it were a SINGLETON then both get_customer_id_request
1514+
and get_customer_profile_request would receive the very same
1515+
Request instance which, in other circumstances, could be a
1516+
desirable effect but not in the example.
1517+
</para>
1518+
1519+
<programlisting><![CDATA[
1520+
1521+
# stdlib
1522+
import uuid4
1523+
1524+
# .. skip Spring Python imports
1525+
1526+
class Request(object):
1527+
def __init__(self, nonce=None, user=None, password=None):
1528+
self.nonce = nonce
1529+
self.user = user
1530+
self.password = password
1531+
1532+
def __str__(self):
1533+
return "<id=%s %s %s %s>" % (hex(id(self)), self.nonce, self.user, self.password)
1534+
1535+
class TestAbstractContext(PythonConfig):
1536+
1537+
@Object(scope.PROTOTYPE, abstract=True)
1538+
def request(self):
1539+
return Request()
1540+
1541+
@Object(parent="request")
1542+
def request_dev(self, req=None):
1543+
req.user = "dev-user"
1544+
req.password = "dev-password"
1545+
1546+
return req
1547+
1548+
@Object(parent="request")
1549+
def request_test(self, req=None):
1550+
req.user = "test-user"
1551+
req.password = "test-password"
1552+
1553+
return req
1554+
1555+
@Object(parent="request_dev")
1556+
def get_customer_id_request(self, req=None):
1557+
req.nonce = uuid4().hex
1558+
1559+
return req
1560+
1561+
@Object(parent="request_test")
1562+
def get_customer_profile_request(self, req=None):
1563+
req.nonce = uuid4().hex
1564+
1565+
return req
1566+
]]></programlisting>
1567+
1568+
<para>
1569+
Same as with the other configuration modes, if you need to get an
1570+
abstract object from a container, use the .get_object's
1571+
<emphasis>ignore_abstract</emphasis> parameter,
1572+
otherwise <emphasis>springpython.container.AbstractObjectException</emphasis>
1573+
will be raised:
1574+
</para>
1575+
1576+
<programlisting><![CDATA[
1577+
# .. skip creating the context
1578+
1579+
# No exception will be raised, even though 'request' is an abstract object
1580+
request = ctx.get_object("request", ignore_abstract=True)
1581+
1582+
# Will show the object
1583+
print request
1584+
1585+
# Will raise AbstractObjectException
1586+
request = ctx.get_object("request")
12531587
]]></programlisting>
12541588

12551589
</section>

0 commit comments

Comments
 (0)