├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── TESTS.md ├── bin ├── config.ini.sample ├── jira_rest.py ├── jira_soap.py ├── jira_xml.py ├── jiracommon.py └── suds │ ├── LICENSE │ ├── README │ ├── __init__.py │ ├── bindings │ ├── __init__.py │ ├── binding.py │ ├── document.py │ ├── multiref.py │ └── rpc.py │ ├── builder.py │ ├── cache.py │ ├── client.py │ ├── metrics.py │ ├── mx │ ├── __init__.py │ ├── appender.py │ ├── basic.py │ ├── core.py │ ├── encoded.py │ ├── literal.py │ └── typer.py │ ├── options.py │ ├── plugin.py │ ├── properties.py │ ├── reader.py │ ├── resolver.py │ ├── sax │ ├── __init__.py │ ├── attribute.py │ ├── date.py │ ├── document.py │ ├── element.py │ ├── enc.py │ ├── parser.py │ └── text.py │ ├── servicedefinition.py │ ├── serviceproxy.py │ ├── soaparray.py │ ├── store.py │ ├── sudsobject.py │ ├── transport │ ├── __init__.py │ ├── http.py │ ├── https.py │ └── options.py │ ├── umx │ ├── __init__.py │ ├── attrlist.py │ ├── basic.py │ ├── core.py │ ├── encoded.py │ └── typed.py │ ├── wsdl.py │ ├── wsse.py │ └── xsd │ ├── __init__.py │ ├── deplist.py │ ├── doctor.py │ ├── query.py │ ├── schema.py │ ├── sxbase.py │ ├── sxbasic.py │ └── sxbuiltin.py ├── default ├── app.conf ├── commands.conf └── jira.conf └── metadata └── default.meta /.gitignore: -------------------------------------------------------------------------------- 1 | bin/config.ini 2 | local/* 3 | metadata/local.meta 4 | *.pyc -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | ## 2.1 5 | 6 | * Add detail option to rapidboards mode 7 | * Fix exception when Labels have the value "Labels" 8 | 9 | ## 2.0.1 10 | 11 | * Add urlencode.quote_plus to arguments so that we don't barf on special characters 12 | 13 | ## 2.0 14 | 15 | * Add jirarest command and documentation 16 | * Rename jira and jiraevents commands to jiraxml and jiraxmlevents respectively - "| jira" is now an alias of jirarest 17 | * jiraxml and jirasoap commands are now deprecated, and will not be developed further (but pull requests are still accepted if there's something you need to have fixed!) 18 | * source is standardized as the command being run: jira_rest, jira_xml, jira_soap 19 | * sourcetype is standardized as the kind of results being returned: jira_issues, jira_filters, jira_sprints, etc. 20 | * index is no longer set in returned events 21 | 22 | ## 1.11 23 | 24 | * Remove _raw field as it was misformatted, and made it impossible to collect results in a summary index 25 | 26 | ## 1.10 27 | 28 | * Add time option to SearchXML and SOAP commands, allows setting _time field to now(), updated, resolved, or created 29 | 30 | ## 1.9 31 | 32 | * Make time_keys and custom_keys configuration parameters optional for the SearchXML command 33 | 34 | ## 1.8 35 | 36 | * Make Protocol and Port configurable 37 | * gitignore .pyc files 38 | 39 | ## 1.7 40 | 41 | * Set _time to now() 42 | * Fix a bug that breaks the main while loop when tempMax is undefined 43 | 44 | ## 1.6 45 | 46 | * Make output of fixVersions prettier in the SOAP command 47 | * Switch from keywords to sys.argv to parse command line and options 48 | * Fix bug in default query 49 | 50 | ## 1.5 51 | 52 | * Make tempMax configurable in SearchXML command 53 | * Make mv fields actually mv in SearchXML command 54 | * Manually construct header in SearchXML command to preserve ordering 55 | 56 | ## 1.4 57 | 58 | * Fix bug with outputResults() 59 | * Move hostname configuration to config.ini 60 | 61 | ## 1.3 62 | 63 | * Add alternate streaming versions of both commands 64 | 65 | ## 1.2 66 | 67 | * Add SOAP command 68 | 69 | ## 1.1 70 | 71 | * Add _time field 72 | 73 | ## 1.0 74 | 75 | * Initial commit of SearchXML command -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Splunk Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Add-on for JIRA 2 | ====================== 3 | 4 | This is a Splunk Add-on for JIRA. 5 | 6 | ## Status 7 | 8 | I am no longer maintaining this app. The version in this repository is older than the newest version available at http://apps.splunk.com/app/1438/. Any bug reports or support requests should probably be made on answers.splunk.com. 9 | 10 | ## Commands 11 | 12 | ### jirarest (REST API) 13 | 14 | #### Syntax 15 | 16 | ``` 17 | | jirarest MODE OPTIONS 18 | ``` 19 | 20 | #### Modes 21 | 22 | * List favorite filters of the configured user 23 | 24 | ``` 25 | | jirarest filters 26 | ``` 27 | 28 | * Run a specific filter and return Issues 29 | 30 | ``` 31 | | jirarest issues FILTER_ID 32 | ``` 33 | 34 | * Run a JQL search and return Issues. 35 | 36 | ``` 37 | | jirarest jqlsearch JQL_QUERY 38 | ``` 39 | 40 | * Run a JQL search and return the all Changes for all matching Issues. 41 | 42 | ``` 43 | | jirarest changelog JQL_QUERY 44 | ``` 45 | 46 | * List rapidboards or sprints (Greenhopper REST API) 47 | 48 | ``` 49 | | jirarest rapidboards list|all|(RAPIDBOARD_ID [detail sprints|issues]) 50 | ``` 51 | 52 | * list will list all scrum boards. 53 | * all will list all sprints in all scrum boards. 54 | * RAPIDBOARD_ID will list all sprints in one specific scrum board. 55 | * "detail sprints" gives details on the active sprints in the rapidboard. 56 | * "detail issues" gives details on the active issues in active sprints including swimlanes and groupings. 57 | * Hint: to get issues in a sprint use jqlquery "sprint=sprint_id" after you have found the desired sprint id here with rapidboards. 58 | 59 | * Pipe search results into a jqlsearch 60 | 61 | ``` 62 | | search ... | eval foo="WTF-1,WTF-2,WTF-3" | makemv delim=, foo | map search="|jirarest batch JQL_QUERY $foo$" 63 | ``` 64 | 65 | * The JQL_QUERY in the batch command is a partial query that ends with the IN keyword, e.g. "key in" 66 | * Results piped in from the preceding search will populate the IN clause. 67 | * Results piped in can be comma- or space- separated 68 | * This is a little ungainly, but quite powerful if you want to pull a list of JIRA keys from an external source and then get all the Issues from JIRA 69 | 70 | #### Options 71 | 72 | * comments 73 | * Shows comments for all Issues returned by main option. 74 | * Compatible with issues, jqlquery, and batch commands. 75 | 76 | * changefield 77 | * By default, pretty names for fields are show. Changefield outputs internal field names instead. 78 | * Compatible with issues, jqlquery and batch commands. 79 | 80 | * changetime TIME_FIELD 81 | * Sets _time to the chosen field. If field does not contain a valid, returns 0 Epoch time 82 | * _time defaults to created if changetime is not set 83 | * Compatible with issues, jqlquery, and batch commands. 84 | 85 | * fields "[INTERNAL_FIELD_NAME,...]" 86 | * Limits the set of fields returned 87 | * Takes a comma-separated list of internal field names. No extra spaces, we're too lazy to trim 88 | * If you want multiple fields, please enclose the field list in double-quotes 89 | * key and created are always returned 90 | 91 | #### Notes 92 | 93 | * The rest command can also be called with | jira. 94 | 95 | ### jirasoap (SOAP API - deprecated) 96 | 97 | #### Syntax 98 | 99 | ``` 100 | | jirasoap MODE OPTIONS 101 | ``` 102 | 103 | #### MODES 104 | 105 | * List all filters available to the logged-in user 106 | 107 | ``` 108 | | jirasoap filters 109 | ``` 110 | 111 | * Run a filter 112 | 113 | ``` 114 | | jirasoap issues [time=TIME_OPTION] FILTER_ID 115 | ``` 116 | 117 | * Run a text search 118 | 119 | ``` 120 | | jirasoap search [time=TIME_OPTION] "foo bar bas" 121 | ``` 122 | 123 | * Run a JQL search 124 | 125 | ``` 126 | | jirasoap jqlsearch [time=TIME_OPTION] JQL_QUERY 127 | ``` 128 | 129 | #### Options 130 | 131 | * time 132 | * *Syntax:* now | updated | created | resolved 133 | * *Description:* By default, _time is set to the current timestamp (now), but if you'd like to have _time reflect one of the native timefields on the issue, you can choose from updated, created, or resolved (if this field is empty, _time will be set to the unix epoch - January 1, 1970 00:00:00 GMT) 134 | 135 | #### Notes 136 | 137 | jirasoap is a 'generating' command that produces a results table. It does not create events. There is also a prototype retainsevents version called 'jirasoapevents' with the same syntax. 138 | It creates real events instead of a table, but the events are empty so far... 139 | 140 | ### jiraxml (SearchRequest XML - deprecated) 141 | 142 | #### Synopsis 143 | 144 | Send a JQL query, return a table with one row for each result 145 | 146 | #### Syntax 147 | 148 | ``` 149 | | jira [time=TIME_OPTION] [JQL_QUERY] 150 | ``` 151 | 152 | #### Arguments 153 | 154 | * time 155 | * *Syntax:* now | updated | created | resolved 156 | * *Description:* By default, _time is set to the current timestamp (now), but if you'd like to have _time reflect one of the native timefields on the issue, you can choose from updated, created, or resolved (if this field is empty, _time will be set to the unix epoch - January 1, 1970 00:00:00 GMT) 157 | * JQL_QUERY 158 | * If omitted, search for all Issues in the default project 159 | 160 | #### Notes 161 | 162 | jiraxml is a 'generating' command that produces a results table. It does not create events. There is also a prototype retainsevents version called 'jiraxmlevents' with the same syntax. 163 | It creates real events instead of a table, but the events are empty so far... 164 | 165 | ## Deployment 166 | 167 | 1. Place the app into $SPLUNK_HOME/etc/apps/jira 168 | 2. Create a folder named local, copy default/jira.conf into local, and update with configuration specific to your instance. 169 | 3. Copy config.ini.sample to config.ini and update with your authentication credentials 170 | 171 | Configure which keys to display in the table with the keys, time_keys, and custom_keys fields. 172 | 173 | * Note that the SOAP API command ignores the key configuration. 174 | 175 | ## Acknowledgements 176 | 177 | * ~~App maintained by Russell Uman~~ 178 | * jirarest command written by Fred de Boer 179 | * jirasoap command written by Fred de Boer 180 | * jiraxml command written by Stephen Sorkin and Jeffrey Isenberg 181 | * The Splunk MySQL app was used as a model, and lots of snippets here were stolen from its commands 182 | * To support the jirasoap command, this App redistributes suds 4.0 https://fedorahosted.org/suds/ 183 | -------------------------------------------------------------------------------- /TESTS.md: -------------------------------------------------------------------------------- 1 | Tests 2 | ===== 3 | 4 | This is a manual test script (which is not the best, but it's significantly better than nothing) 5 | 6 | Please test each of these queries, substituting variables for ones that make sense for your JIRA installation, before each 7 | release or pull request. | table helps verify. 8 | 9 | ## jirarest 10 | 11 | | jirarest filters 12 | * Should return a list of filters favorited by the jira user. 13 | 14 | | jirarest issues FILTER_ID 15 | * Should return a list of issues. Use an id from the filters mode. 16 | 17 | | jirarest jqlsearch JQL_QUERY 18 | * Should return a list of issues for some reasonable query. 19 | 20 | | jirarest jqlsearch JQL_QUERY comments 21 | * Should return a list of comments on the issues returned by the query. 22 | 23 | | jirarest jqlsearch JQL_QUERY changefield 24 | * Should return a list of issues with internal field names instead of pretty field names. 25 | 26 | | jirarest jqlsearch JQL_QUERY changetime updated 27 | * Should return a list of issues with _time set to updated instead of created. 28 | 29 | | jirarest jqlsearch JQL_QUERY comments changefield changetime updated 30 | * All together now. 31 | 32 | | jirarest jqlsearch JQL_QUERY fields "status,duedate" 33 | * Should return a list of issues with just key, created, status, and duedate fields. 34 | 35 | | jirarest changelog JQL_QUERY 36 | * Should return a list of changes (history) for the issues returned by the query. 37 | 38 | | jirarest rapidboards list 39 | * Should return a list of all rapidboards. 40 | 41 | | jirarest rapidboards all 42 | * Should return a list of all sprints in all rapidboards. 43 | 44 | | jirarest rapidboards RAPIDBOARD_ID 45 | * Should return a list of all issues in a specific rapidboard. Use an id from the list option. 46 | 47 | | jirarest rapidboards RAPIDBOARD_ID detail sprints 48 | * Should return a list of all sprints in a specific rapidboard. Use an id from the list option. 49 | 50 | | jirarest rapidboards RAPIDBOARD_ID detail issues 51 | * Should return a list of all active issues in a specific rapidboard, including column and swimlane information. 52 | Use an id from the list option. 53 | 54 | ... | jirarest batch JQL_QUERY $foo$ 55 | * I don't really know how to test this I should get an example from Fred. 56 | 57 | ## jirasoap 58 | 59 | ## jiraxml -------------------------------------------------------------------------------- /bin/config.ini.sample: -------------------------------------------------------------------------------- 1 | [jira] 2 | hostname = jira.example.com 3 | username = admin 4 | password = changeme 5 | jira_protocol = https 6 | jira_port = 443 7 | soap_protocol = https 8 | soap_port = 8080 9 | maxresults = 100 -------------------------------------------------------------------------------- /bin/jira_soap.py: -------------------------------------------------------------------------------- 1 | """ 2 | External search command for querying the JIRA SOAP API 3 | 4 | Usage: 5 | | jirasoap search "text search" | ... ... # text search 6 | | jirasoap jqlsearch "JQL query" | ... ... # JQL search* 7 | | jirasoap issues | ... ... # filter search 8 | | jirasoap filters | ... ... # list all filters (to get ids for issues command) 9 | 10 | * The jqlsearch doesn't handle '=' very well - splunk parses these as options. 11 | Instead of saying "project = foo AND status = Open" say "project in (foo) AND status in (Open)". 12 | Or use the SearchRequest XML command instead 13 | 14 | Author: Fred de Boer 15 | Author: Jeffrey Isenberg 16 | Author: Russell Uman 17 | """ 18 | 19 | import splunk.Intersplunk as isp 20 | import splunk.mining.dcutils as dcu 21 | 22 | import jiracommon 23 | import logging 24 | import sys 25 | import time 26 | 27 | from suds.client import Client 28 | 29 | try: 30 | messages = {} 31 | logging.getLogger('suds').setLevel(logging.INFO) 32 | 33 | logger = dcu.getLogger() 34 | 35 | # Get configuration values from config.ini 36 | local_conf = jiracommon.getLocalConf() 37 | 38 | hostname = local_conf.get('jira', 'hostname') 39 | username = local_conf.get('jira', 'username') 40 | password = local_conf.get('jira', 'password') 41 | protocol = local_conf.get('jira', 'soap_protocol'); 42 | port = local_conf.get('jira', 'soap_port'); 43 | 44 | url = "%s://%s:%s/rpc/soap/jirasoapservice-v2?wsdl" % (protocol, hostname, port) 45 | logger.info(url) 46 | client = Client(url) 47 | auth = client.service.login(username, password) 48 | 49 | keywords, argvals = isp.getKeywordsAndOptions() 50 | 51 | time_option = argvals.get('time', "now") 52 | 53 | logger.info('argv: ' + str(sys.argv)) 54 | 55 | if sys.argv[1] == 'filters': 56 | filters = client.service.getFavouriteFilters(auth) 57 | 58 | keys = (('author', None), ('id', None), ('name', None)) 59 | 60 | results = [] 61 | for filter in filters: 62 | row = jiracommon.flatten(filter, keys) 63 | logger.info(time.time()) 64 | row['host'] = hostname 65 | row['source'] = "jira_soap" 66 | row['sourcetype'] = "jira_filters" 67 | row['_time'] = int(time.time()) 68 | results.append(row) 69 | isp.outputResults(results) 70 | sys.exit(0) 71 | 72 | elif sys.argv[1] == 'issues': 73 | filter_id = sys.argv[-1] 74 | issues = client.service.getIssuesFromFilter(auth, filter_id) 75 | # TODO this 1000 issue max isn't working as expected - if there are more than 1000 results, no results are returned 76 | elif sys.argv[1] == 'search': 77 | search = sys.argv[-1] 78 | issues = (client.service.getIssuesFromTextSearch(auth, search, 1000) ) 79 | elif sys.argv[1] == 'jqlsearch': 80 | jql = sys.argv[-1] 81 | issues = (client.service.getIssuesFromJqlSearch(auth, jql, 1000) ) 82 | else: 83 | logger.fatal('invalid command') 84 | sys.exit(1) 85 | 86 | statuses = jiracommon.api_to_dict(client.service.getStatuses(auth)) 87 | resolutions = jiracommon.api_to_dict(client.service.getResolutions(auth)) 88 | priorities = jiracommon.api_to_dict(client.service.getPriorities(auth)) 89 | 90 | resolutions[None] = 'UNRESOLVED' 91 | 92 | results = [] 93 | 94 | keys = (('assignee', None), 95 | ('description', None), 96 | ('key', None), 97 | ('summary', None), 98 | ('reporter', None), 99 | ('status', statuses), 100 | ('resolution', resolutions), 101 | ('priority', priorities), 102 | ('project', None), 103 | ('type', None), 104 | ('created', None), 105 | ('updated', None)) 106 | for issue in issues: 107 | row = jiracommon.flatten(issue, keys) 108 | 109 | # Special handling for multi-value fields 110 | affectedVersions = [] 111 | for f in issue['affectsVersions']: 112 | affectedVersions.append(f['name']) 113 | row['affectedVersions'] = affectedVersions 114 | 115 | fixVersions = [] 116 | for f in issue['fixVersions']: 117 | fixVersions.append(f['name']) 118 | row['fixVersions'] = fixVersions 119 | 120 | # Custom fields 121 | for f in issue['customFieldValues']: 122 | if f['customfieldId'] == "customfield_10020": 123 | row['SFDCcase'] = f['values'] 124 | if f['customfieldId'] == "customfield_10091": 125 | row['TargetRelease'] = f['values'] 126 | 127 | row['host'] = hostname 128 | row['source'] = 'jira_soap' 129 | row['sourcetype'] = "jira_issues" 130 | 131 | # override _time if time argument is set 132 | if time_option == "now": 133 | row['_time'] = int(time.time()) 134 | else: 135 | row['_time'] = int(time.mktime(time.strptime(row[time_option], '%Y-%m-%d %H:%M:%S'))) 136 | 137 | results.append(row) 138 | 139 | isp.outputResults(results) 140 | 141 | except Exception, e: 142 | logger.exception(str(e)) 143 | isp.generateErrorResults(str(e)) -------------------------------------------------------------------------------- /bin/jira_xml.py: -------------------------------------------------------------------------------- 1 | """ 2 | External search command for querying the JIRA SearchRequest XML endpoint 3 | 4 | Usage: 5 | | jira [time=TIME_OPTION JQL_QUERY | ... ... 6 | 7 | Author: Stephen Sorkin 8 | Author: Russell Uman 9 | """ 10 | 11 | import base64 12 | import datetime 13 | import lxml.etree as et 14 | import re 15 | import sys 16 | import time 17 | import urllib 18 | import urllib2 19 | 20 | import jiracommon 21 | 22 | import splunk.mining.dcutils as dcu 23 | import splunk.Intersplunk as isp 24 | 25 | try: 26 | messages = {} 27 | logger = dcu.getLogger() 28 | 29 | # Get configuration values from jira.conf 30 | splunk_conf = jiracommon.getSplunkConf() 31 | 32 | keys = splunk_conf.get('keys', '').split(',') 33 | time_keys = splunk_conf.get('time_keys', '').split(',') 34 | custom_keys = splunk_conf.get('custom_keys', '').split(',') 35 | 36 | offset = 0 37 | count = int(splunk_conf.get('tempMax', 1000)) 38 | 39 | # Get configuration values from config.ini 40 | local_conf = jiracommon.getLocalConf() 41 | 42 | hostname = local_conf.get('jira', 'hostname') 43 | username = local_conf.get('jira', 'username') 44 | password = local_conf.get('jira', 'password') 45 | protocol = local_conf.get('jira', 'jira_protocol') 46 | port = local_conf.get('jira', 'jira_port') 47 | 48 | keywords, argvals = isp.getKeywordsAndOptions() 49 | logger.info('keywords: %s' % keywords) 50 | logger.info('argvals: %s' % argvals) 51 | logger.info('argv: %s' % sys.argv) 52 | 53 | # jql must be the last argument (but it's optional) 54 | if len(sys.argv) > 1: 55 | jql = sys.argv[-1] 56 | # default is to search for all issues in the default project 57 | else: 58 | jql = "project=%s" % splunk_conf.get('default_project') 59 | logger.info('jql: %s' % jql) 60 | 61 | time_option = argvals.get('time', "now") 62 | logger.info('time: %s' % time) 63 | 64 | results = [] 65 | 66 | header = ['_time'] 67 | for k in keys: 68 | header.append(k) 69 | for k in time_keys: 70 | header.append(k) 71 | for k in custom_keys: 72 | header.append(k) 73 | header.extend(['host', 'source', 'sourcetype']) 74 | 75 | while True: 76 | query = urllib.urlencode({'jqlQuery':jql, 'tempMax':count, 'pager/start':offset}) 77 | 78 | url = "%s://%s:%s/sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml?%s" % (protocol, hostname, port, query) 79 | request = urllib2.Request(url) 80 | logger.info(url) 81 | 82 | request.add_header('Authorization', "Basic %s" % base64.b64encode("%s:%s" % (username, password))) 83 | result = urllib2.urlopen(request) 84 | 85 | root = et.parse(result) 86 | 87 | added_count = 0 88 | for elem in root.iter('item'): 89 | added_count = added_count + 1 90 | row = {} 91 | 92 | for k in keys: 93 | v = elem.xpath(k) 94 | if len(v) > 1: 95 | row[k] = [val.text for val in v] 96 | if '__mv_' + k not in header: 97 | header.append('__mv_' + k) 98 | elif len(v) == 1: 99 | row[k] = v[0].text 100 | 101 | for k in time_keys: 102 | # If time_keys is empty, then the split above results in [''] 103 | if k: 104 | v = elem.xpath(k) 105 | if len(v) == 1: 106 | row[k] = v[0].get("seconds") 107 | 108 | for k in custom_keys: 109 | # If custom_keys is empty, then the split above results in [''] 110 | if k: 111 | v = elem.xpath('customfields/customfield/customfieldvalues/customfieldvalue[../../customfieldname/text() = "%s"]' % k) 112 | if len(v) > 1: 113 | row[k] = [val.text for val in v] 114 | if '__mv_' + k not in header: 115 | header.append('__mv_' + k) 116 | elif len(v) == 1: 117 | row[k] = v[0].text 118 | 119 | v = elem.xpath('customfields/customfield/customfieldvalues/label[../../customfieldname/text() = "%s"]' % k) 120 | if len(v) > 1: 121 | row[k] = [val.text for val in v] 122 | if '__mv_' + k not in header: 123 | header.append('__mv_' + k) 124 | elif len(v) == 1: 125 | row[k] = v[0].text 126 | 127 | # override _time if time argument is set 128 | if time_option == "now": 129 | row['_time'] = int(time.time()) 130 | elif time_option in keys: 131 | time_text = elem.findtext(time_option) 132 | if time_text != None: 133 | logger.info("time text: %s" % time_text) 134 | time_value = re.sub(r' (\+|-)\d+$', '', elem.findtext(time_option)) 135 | timestamp = time.mktime(datetime.datetime.strptime(time_value, "%a, %d %b %Y %H:%M:%S").timetuple()) 136 | row['_time'] = timestamp 137 | else: 138 | row['_time'] = 0 139 | else: 140 | row['_time'] = 0 141 | 142 | row['host'] = hostname 143 | row['source'] = 'jira_xml' 144 | row['sourcetype'] = 'jira_issues' 145 | 146 | results.append(row) 147 | 148 | if added_count > 0: 149 | offset = offset + added_count 150 | 151 | if added_count < count: 152 | break 153 | 154 | isp.outputResults(results, None, header) 155 | 156 | except Exception, e: 157 | logger.exception(str(e)) 158 | isp.generateErrorResults(str(e)) -------------------------------------------------------------------------------- /bin/jiracommon.py: -------------------------------------------------------------------------------- 1 | import ConfigParser 2 | import os 3 | import splunk.bundle as sb 4 | import splunk.Intersplunk as isp 5 | 6 | def getSplunkConf(): 7 | results, dummyresults, settings = isp.getOrganizedResults() 8 | namespace = settings.get("namespace", None) 9 | owner = settings.get("owner", None) 10 | sessionKey = settings.get("sessionKey", None) 11 | 12 | conf = sb.getConf('jira', namespace=namespace, owner=owner, sessionKey=sessionKey) 13 | stanza = conf.get('jira') 14 | 15 | return stanza 16 | 17 | def getLocalConf(): 18 | local_conf = ConfigParser.ConfigParser() 19 | location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) 20 | local_conf.read(location + '/config.ini') 21 | 22 | return local_conf 23 | 24 | def flatten(item, keys): 25 | response = {} 26 | for (key, replacer) in keys: 27 | if not replacer: 28 | response[key] = str(item[key]) 29 | else: 30 | response[key] = replacer.get(item[key], item[key]) 31 | 32 | return response 33 | 34 | def api_to_dict(apidata): 35 | dictdata = {} 36 | for item in apidata: 37 | dictdata[item['id']] = item['name'] 38 | return dictdata -------------------------------------------------------------------------------- /bin/suds/LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /bin/suds/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Suds is a lightweight SOAP python client that provides a 19 | service proxy for Web Services. 20 | """ 21 | 22 | import os 23 | import sys 24 | 25 | # 26 | # Project properties 27 | # 28 | 29 | __version__ = '0.4' 30 | __build__="GA R699-20100913" 31 | 32 | # 33 | # Exceptions 34 | # 35 | 36 | class MethodNotFound(Exception): 37 | def __init__(self, name): 38 | Exception.__init__(self, "Method not found: '%s'" % name) 39 | 40 | class PortNotFound(Exception): 41 | def __init__(self, name): 42 | Exception.__init__(self, "Port not found: '%s'" % name) 43 | 44 | class ServiceNotFound(Exception): 45 | def __init__(self, name): 46 | Exception.__init__(self, "Service not found: '%s'" % name) 47 | 48 | class TypeNotFound(Exception): 49 | def __init__(self, name): 50 | Exception.__init__(self, "Type not found: '%s'" % tostr(name)) 51 | 52 | class BuildError(Exception): 53 | msg = \ 54 | """ 55 | An error occured while building a instance of (%s). As a result 56 | the object you requested could not be constructed. It is recommended 57 | that you construct the type manually using a Suds object. 58 | Please open a ticket with a description of this error. 59 | Reason: %s 60 | """ 61 | def __init__(self, name, exception): 62 | Exception.__init__(self, BuildError.msg % (name, exception)) 63 | 64 | class SoapHeadersNotPermitted(Exception): 65 | msg = \ 66 | """ 67 | Method (%s) was invoked with SOAP headers. The WSDL does not 68 | define SOAP headers for this method. Retry without the soapheaders 69 | keyword argument. 70 | """ 71 | def __init__(self, name): 72 | Exception.__init__(self, self.msg % name) 73 | 74 | class WebFault(Exception): 75 | def __init__(self, fault, document): 76 | if hasattr(fault, 'faultstring'): 77 | Exception.__init__(self, "Server raised fault: '%s'" % fault.faultstring) 78 | self.fault = fault 79 | self.document = document 80 | 81 | # 82 | # Logging 83 | # 84 | 85 | class Repr: 86 | def __init__(self, x): 87 | self.x = x 88 | def __str__(self): 89 | return repr(self.x) 90 | 91 | # 92 | # Utility 93 | # 94 | 95 | def tostr(object, encoding=None): 96 | """ get a unicode safe string representation of an object """ 97 | if isinstance(object, basestring): 98 | if encoding is None: 99 | return object 100 | else: 101 | return object.encode(encoding) 102 | if isinstance(object, tuple): 103 | s = ['('] 104 | for item in object: 105 | if isinstance(item, basestring): 106 | s.append(item) 107 | else: 108 | s.append(tostr(item)) 109 | s.append(', ') 110 | s.append(')') 111 | return ''.join(s) 112 | if isinstance(object, list): 113 | s = ['['] 114 | for item in object: 115 | if isinstance(item, basestring): 116 | s.append(item) 117 | else: 118 | s.append(tostr(item)) 119 | s.append(', ') 120 | s.append(']') 121 | return ''.join(s) 122 | if isinstance(object, dict): 123 | s = ['{'] 124 | for item in object.items(): 125 | if isinstance(item[0], basestring): 126 | s.append(item[0]) 127 | else: 128 | s.append(tostr(item[0])) 129 | s.append(' = ') 130 | if isinstance(item[1], basestring): 131 | s.append(item[1]) 132 | else: 133 | s.append(tostr(item[1])) 134 | s.append(', ') 135 | s.append('}') 136 | return ''.join(s) 137 | try: 138 | return unicode(object) 139 | except: 140 | return str(object) 141 | 142 | class null: 143 | """ 144 | The I{null} object. 145 | Used to pass NULL for optional XML nodes. 146 | """ 147 | pass 148 | 149 | def objid(obj): 150 | return obj.__class__.__name__\ 151 | +':'+hex(id(obj)) 152 | 153 | 154 | import client 155 | -------------------------------------------------------------------------------- /bin/suds/bindings/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides modules containing classes to support Web Services (SOAP) 19 | bindings. 20 | """ -------------------------------------------------------------------------------- /bin/suds/bindings/document.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides classes for the (WS) SOAP I{document/literal}. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.bindings.binding import Binding 24 | from suds.sax.element import Element 25 | 26 | log = getLogger(__name__) 27 | 28 | 29 | class Document(Binding): 30 | """ 31 | The document/literal style. Literal is the only (@use) supported 32 | since document/encoded is pretty much dead. 33 | Although the soap specification supports multiple documents within the soap 34 | , it is very uncommon. As such, suds presents an I{RPC} view of 35 | service methods defined with a single document parameter. This is done so 36 | that the user can pass individual parameters instead of one, single document. 37 | To support the complete specification, service methods defined with multiple documents 38 | (multiple message parts), must present a I{document} view for that method. 39 | """ 40 | 41 | def bodycontent(self, method, args, kwargs): 42 | # 43 | # The I{wrapped} vs I{bare} style is detected in 2 ways. 44 | # If there is 2+ parts in the message then it is I{bare}. 45 | # If there is only (1) part and that part resolves to a builtin then 46 | # it is I{bare}. Otherwise, it is I{wrapped}. 47 | # 48 | if not len(method.soap.input.body.parts): 49 | return () 50 | wrapped = method.soap.input.body.wrapped 51 | if wrapped: 52 | pts = self.bodypart_types(method) 53 | root = self.document(pts[0]) 54 | else: 55 | root = [] 56 | n = 0 57 | for pd in self.param_defs(method): 58 | if n < len(args): 59 | value = args[n] 60 | else: 61 | value = kwargs.get(pd[0]) 62 | n += 1 63 | p = self.mkparam(method, pd, value) 64 | if p is None: 65 | continue 66 | if not wrapped: 67 | ns = pd[1].namespace('ns0') 68 | p.setPrefix(ns[0], ns[1]) 69 | root.append(p) 70 | return root 71 | 72 | def replycontent(self, method, body): 73 | wrapped = method.soap.output.body.wrapped 74 | if wrapped: 75 | return body[0].children 76 | else: 77 | return body.children 78 | 79 | def document(self, wrapper): 80 | """ 81 | Get the document root. For I{document/literal}, this is the 82 | name of the wrapper element qualifed by the schema tns. 83 | @param wrapper: The method name. 84 | @type wrapper: L{xsd.sxbase.SchemaObject} 85 | @return: A root element. 86 | @rtype: L{Element} 87 | """ 88 | tag = wrapper[1].name 89 | ns = wrapper[1].namespace('ns0') 90 | d = Element(tag, ns=ns) 91 | return d 92 | 93 | def mkparam(self, method, pdef, object): 94 | # 95 | # Expand list parameters into individual parameters 96 | # each with the type information. This is because in document 97 | # arrays are simply unbounded elements. 98 | # 99 | if isinstance(object, (list, tuple)): 100 | tags = [] 101 | for item in object: 102 | tags.append(self.mkparam(method, pdef, item)) 103 | return tags 104 | else: 105 | return Binding.mkparam(self, method, pdef, object) 106 | 107 | def param_defs(self, method): 108 | # 109 | # Get parameter definitions for document literal. 110 | # The I{wrapped} vs I{bare} style is detected in 2 ways. 111 | # If there is 2+ parts in the message then it is I{bare}. 112 | # If there is only (1) part and that part resolves to a builtin then 113 | # it is I{bare}. Otherwise, it is I{wrapped}. 114 | # 115 | pts = self.bodypart_types(method) 116 | wrapped = method.soap.input.body.wrapped 117 | if not wrapped: 118 | return pts 119 | result = [] 120 | # wrapped 121 | for p in pts: 122 | resolved = p[1].resolve() 123 | for child, ancestry in resolved: 124 | if child.isattr(): 125 | continue 126 | if self.bychoice(ancestry): 127 | log.debug( 128 | '%s\ncontained by , excluded as param for %s()', 129 | child, 130 | method.name) 131 | continue 132 | result.append((child.name, child)) 133 | return result 134 | 135 | def returned_types(self, method): 136 | result = [] 137 | wrapped = method.soap.output.body.wrapped 138 | rts = self.bodypart_types(method, input=False) 139 | if wrapped: 140 | for pt in rts: 141 | resolved = pt.resolve(nobuiltin=True) 142 | for child, ancestry in resolved: 143 | result.append(child) 144 | break 145 | else: 146 | result += rts 147 | return result 148 | 149 | def bychoice(self, ancestry): 150 | """ 151 | The ancestry contains a 152 | @param ancestry: A list of ancestors. 153 | @type ancestry: list 154 | @return: True if contains 155 | @rtype: boolean 156 | """ 157 | for x in ancestry: 158 | if x.choice(): 159 | return True 160 | return False -------------------------------------------------------------------------------- /bin/suds/bindings/multiref.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides classes for handling soap multirefs. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sax.element import Element 24 | 25 | log = getLogger(__name__) 26 | 27 | soapenc = (None, 'http://schemas.xmlsoap.org/soap/encoding/') 28 | 29 | class MultiRef: 30 | """ 31 | Resolves and replaces multirefs. 32 | @ivar nodes: A list of non-multiref nodes. 33 | @type nodes: list 34 | @ivar catalog: A dictionary of multiref nodes by id. 35 | @type catalog: dict 36 | """ 37 | 38 | def __init__(self): 39 | self.nodes = [] 40 | self.catalog = {} 41 | 42 | def process(self, body): 43 | """ 44 | Process the specified soap envelope body and replace I{multiref} node 45 | references with the contents of the referenced node. 46 | @param body: A soap envelope body node. 47 | @type body: L{Element} 48 | @return: The processed I{body} 49 | @rtype: L{Element} 50 | """ 51 | self.nodes = [] 52 | self.catalog = {} 53 | self.build_catalog(body) 54 | self.update(body) 55 | body.children = self.nodes 56 | return body 57 | 58 | def update(self, node): 59 | """ 60 | Update the specified I{node} by replacing the I{multiref} references with 61 | the contents of the referenced nodes and remove the I{href} attribute. 62 | @param node: A node to update. 63 | @type node: L{Element} 64 | @return: The updated node 65 | @rtype: L{Element} 66 | """ 67 | self.replace_references(node) 68 | for c in node.children: 69 | self.update(c) 70 | return node 71 | 72 | def replace_references(self, node): 73 | """ 74 | Replacing the I{multiref} references with the contents of the 75 | referenced nodes and remove the I{href} attribute. Warning: since 76 | the I{ref} is not cloned, 77 | @param node: A node to update. 78 | @type node: L{Element} 79 | """ 80 | href = node.getAttribute('href') 81 | if href is None: 82 | return 83 | id = href.getValue() 84 | ref = self.catalog.get(id) 85 | if ref is None: 86 | log.error('soap multiref: %s, not-resolved', id) 87 | return 88 | node.append(ref.children) 89 | node.setText(ref.getText()) 90 | for a in ref.attributes: 91 | if a.name != 'id': 92 | node.append(a) 93 | node.remove(href) 94 | 95 | def build_catalog(self, body): 96 | """ 97 | Create the I{catalog} of multiref nodes by id and the list of 98 | non-multiref nodes. 99 | @param body: A soap envelope body node. 100 | @type body: L{Element} 101 | """ 102 | for child in body.children: 103 | if self.soaproot(child): 104 | self.nodes.append(child) 105 | id = child.get('id') 106 | if id is None: continue 107 | key = '#%s' % id 108 | self.catalog[key] = child 109 | 110 | def soaproot(self, node): 111 | """ 112 | Get whether the specified I{node} is a soap encoded root. 113 | This is determined by examining @soapenc:root='1'. 114 | The node is considered to be a root when the attribute 115 | is not specified. 116 | @param node: A node to evaluate. 117 | @type node: L{Element} 118 | @return: True if a soap encoded root. 119 | @rtype: bool 120 | """ 121 | root = node.getAttribute('root', ns=soapenc) 122 | if root is None: 123 | return True 124 | else: 125 | return ( root.value == '1' ) 126 | -------------------------------------------------------------------------------- /bin/suds/bindings/rpc.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides classes for the (WS) SOAP I{rpc/literal} and I{rpc/encoded} bindings. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx.encoded import Encoded as MxEncoded 24 | from suds.umx.encoded import Encoded as UmxEncoded 25 | from suds.bindings.binding import Binding, envns 26 | from suds.sax.element import Element 27 | 28 | log = getLogger(__name__) 29 | 30 | 31 | encns = ('SOAP-ENC', 'http://schemas.xmlsoap.org/soap/encoding/') 32 | 33 | class RPC(Binding): 34 | """ 35 | RPC/Literal binding style. 36 | """ 37 | 38 | def param_defs(self, method): 39 | return self.bodypart_types(method) 40 | 41 | def envelope(self, header, body): 42 | env = Binding.envelope(self, header, body) 43 | env.addPrefix(encns[0], encns[1]) 44 | env.set('%s:encodingStyle' % envns[0], 45 | 'http://schemas.xmlsoap.org/soap/encoding/') 46 | return env 47 | 48 | def bodycontent(self, method, args, kwargs): 49 | n = 0 50 | root = self.method(method) 51 | for pd in self.param_defs(method): 52 | if n < len(args): 53 | value = args[n] 54 | else: 55 | value = kwargs.get(pd[0]) 56 | p = self.mkparam(method, pd, value) 57 | if p is not None: 58 | root.append(p) 59 | n += 1 60 | return root 61 | 62 | def replycontent(self, method, body): 63 | return body[0].children 64 | 65 | def method(self, method): 66 | """ 67 | Get the document root. For I{rpc/(literal|encoded)}, this is the 68 | name of the method qualifed by the schema tns. 69 | @param method: A service method. 70 | @type method: I{service.Method} 71 | @return: A root element. 72 | @rtype: L{Element} 73 | """ 74 | ns = method.soap.input.body.namespace 75 | if ns[0] is None: 76 | ns = ('ns0', ns[1]) 77 | method = Element(method.name, ns=ns) 78 | return method 79 | 80 | 81 | class Encoded(RPC): 82 | """ 83 | RPC/Encoded (section 5) binding style. 84 | """ 85 | 86 | def marshaller(self): 87 | return MxEncoded(self.schema()) 88 | 89 | def unmarshaller(self, typed=True): 90 | """ 91 | Get the appropriate XML decoder. 92 | @return: Either the (basic|typed) unmarshaller. 93 | @rtype: L{UmxTyped} 94 | """ 95 | if typed: 96 | return UmxEncoded(self.schema()) 97 | else: 98 | return RPC.unmarshaller(self, typed) 99 | -------------------------------------------------------------------------------- /bin/suds/builder.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{builder} module provides an wsdl/xsd defined types factory 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sudsobject import Factory 24 | 25 | log = getLogger(__name__) 26 | 27 | 28 | class Builder: 29 | """ Builder used to construct an object for types defined in the schema """ 30 | 31 | def __init__(self, resolver): 32 | """ 33 | @param resolver: A schema object name resolver. 34 | @type resolver: L{resolver.Resolver} 35 | """ 36 | self.resolver = resolver 37 | 38 | def build(self, name): 39 | """ build a an object for the specified typename as defined in the schema """ 40 | if isinstance(name, basestring): 41 | type = self.resolver.find(name) 42 | if type is None: 43 | raise TypeNotFound(name) 44 | else: 45 | type = name 46 | cls = type.name 47 | if type.mixed(): 48 | data = Factory.property(cls) 49 | else: 50 | data = Factory.object(cls) 51 | resolved = type.resolve() 52 | md = data.__metadata__ 53 | md.sxtype = resolved 54 | md.ordering = self.ordering(resolved) 55 | history = [] 56 | self.add_attributes(data, resolved) 57 | for child, ancestry in type.children(): 58 | if self.skip_child(child, ancestry): 59 | continue 60 | self.process(data, child, history[:]) 61 | return data 62 | 63 | def process(self, data, type, history): 64 | """ process the specified type then process its children """ 65 | if type in history: 66 | return 67 | if type.enum(): 68 | return 69 | history.append(type) 70 | resolved = type.resolve() 71 | value = None 72 | if type.unbounded(): 73 | value = [] 74 | else: 75 | if len(resolved) > 0: 76 | if resolved.mixed(): 77 | value = Factory.property(resolved.name) 78 | md = value.__metadata__ 79 | md.sxtype = resolved 80 | else: 81 | value = Factory.object(resolved.name) 82 | md = value.__metadata__ 83 | md.sxtype = resolved 84 | md.ordering = self.ordering(resolved) 85 | setattr(data, type.name, value) 86 | if value is not None: 87 | data = value 88 | if not isinstance(data, list): 89 | self.add_attributes(data, resolved) 90 | for child, ancestry in resolved.children(): 91 | if self.skip_child(child, ancestry): 92 | continue 93 | self.process(data, child, history[:]) 94 | 95 | def add_attributes(self, data, type): 96 | """ add required attributes """ 97 | for attr, ancestry in type.attributes(): 98 | name = '_%s' % attr.name 99 | value = attr.get_default() 100 | setattr(data, name, value) 101 | 102 | def skip_child(self, child, ancestry): 103 | """ get whether or not to skip the specified child """ 104 | if child.any(): return True 105 | for x in ancestry: 106 | if x.choice(): 107 | return True 108 | return False 109 | 110 | def ordering(self, type): 111 | """ get the ordering """ 112 | result = [] 113 | for child, ancestry in type.resolve(): 114 | name = child.name 115 | if child.name is None: 116 | continue 117 | if child.isattr(): 118 | name = '_%s' % child.name 119 | result.append(name) 120 | return result 121 | -------------------------------------------------------------------------------- /bin/suds/metrics.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{metrics} module defines classes and other resources 19 | designed for collecting and reporting performance metrics. 20 | """ 21 | 22 | import time 23 | from logging import getLogger 24 | from suds import * 25 | from math import modf 26 | 27 | log = getLogger(__name__) 28 | 29 | class Timer: 30 | 31 | def __init__(self): 32 | self.started = 0 33 | self.stopped = 0 34 | 35 | def start(self): 36 | self.started = time.time() 37 | self.stopped = 0 38 | return self 39 | 40 | def stop(self): 41 | if self.started > 0: 42 | self.stopped = time.time() 43 | return self 44 | 45 | def duration(self): 46 | return ( self.stopped - self.started ) 47 | 48 | def __str__(self): 49 | if self.started == 0: 50 | return 'not-running' 51 | if self.started > 0 and self.stopped == 0: 52 | return 'started: %d (running)' % self.started 53 | duration = self.duration() 54 | jmod = ( lambda m : (m[1], m[0]*1000) ) 55 | if duration < 1: 56 | ms = (duration*1000) 57 | return '%d (ms)' % ms 58 | if duration < 60: 59 | m = modf(duration) 60 | return '%d.%.3d (seconds)' % jmod(m) 61 | m = modf(duration/60) 62 | return '%d.%.3d (minutes)' % jmod(m) 63 | -------------------------------------------------------------------------------- /bin/suds/mx/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides modules containing classes to support 19 | marshalling (XML). 20 | """ 21 | 22 | from suds.sudsobject import Object 23 | 24 | 25 | class Content(Object): 26 | """ 27 | Marshaller Content. 28 | @ivar tag: The content tag. 29 | @type tag: str 30 | @ivar value: The content's value. 31 | @type value: I{any} 32 | """ 33 | 34 | extensions = [] 35 | 36 | def __init__(self, tag=None, value=None, **kwargs): 37 | """ 38 | @param tag: The content tag. 39 | @type tag: str 40 | @param value: The content's value. 41 | @type value: I{any} 42 | """ 43 | Object.__init__(self) 44 | self.tag = tag 45 | self.value = value 46 | for k,v in kwargs.items(): 47 | setattr(self, k, v) 48 | 49 | def __getattr__(self, name): 50 | if name not in self.__dict__: 51 | if name in self.extensions: 52 | v = None 53 | setattr(self, name, v) 54 | else: 55 | raise AttributeError, \ 56 | 'Content has no attribute %s' % name 57 | else: 58 | v = self.__dict__[name] 59 | return v -------------------------------------------------------------------------------- /bin/suds/mx/basic.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides basic I{marshaller} classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx import * 24 | from suds.mx.core import Core 25 | 26 | log = getLogger(__name__) 27 | 28 | 29 | class Basic(Core): 30 | """ 31 | A I{basic} (untyped) marshaller. 32 | """ 33 | 34 | def process(self, value, tag=None): 35 | """ 36 | Process (marshal) the tag with the specified value using the 37 | optional type information. 38 | @param value: The value (content) of the XML node. 39 | @type value: (L{Object}|any) 40 | @param tag: The (optional) tag name for the value. The default is 41 | value.__class__.__name__ 42 | @type tag: str 43 | @return: An xml node. 44 | @rtype: L{Element} 45 | """ 46 | content = Content(tag=tag, value=value) 47 | result = Core.process(self, content) 48 | return result -------------------------------------------------------------------------------- /bin/suds/mx/core.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides I{marshaller} core classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx import * 24 | from suds.mx.appender import ContentAppender 25 | from suds.sax.element import Element 26 | from suds.sax.document import Document 27 | from suds.sudsobject import Property 28 | 29 | 30 | log = getLogger(__name__) 31 | 32 | 33 | class Core: 34 | """ 35 | An I{abstract} marshaller. This class implement the core 36 | functionality of the marshaller. 37 | @ivar appender: A content appender. 38 | @type appender: L{ContentAppender} 39 | """ 40 | 41 | def __init__(self): 42 | """ 43 | """ 44 | self.appender = ContentAppender(self) 45 | 46 | def process(self, content): 47 | """ 48 | Process (marshal) the tag with the specified value using the 49 | optional type information. 50 | @param content: The content to process. 51 | @type content: L{Object} 52 | """ 53 | log.debug('processing:\n%s', content) 54 | self.reset() 55 | if content.tag is None: 56 | content.tag = content.value.__class__.__name__ 57 | document = Document() 58 | if isinstance(content.value, Property): 59 | root = self.node(content) 60 | self.append(document, content) 61 | else: 62 | self.append(document, content) 63 | return document.root() 64 | 65 | def append(self, parent, content): 66 | """ 67 | Append the specified L{content} to the I{parent}. 68 | @param parent: The parent node to append to. 69 | @type parent: L{Element} 70 | @param content: The content to append. 71 | @type content: L{Object} 72 | """ 73 | log.debug('appending parent:\n%s\ncontent:\n%s', parent, content) 74 | if self.start(content): 75 | self.appender.append(parent, content) 76 | self.end(parent, content) 77 | 78 | def reset(self): 79 | """ 80 | Reset the marshaller. 81 | """ 82 | pass 83 | 84 | def node(self, content): 85 | """ 86 | Create and return an XML node. 87 | @param content: The content for which proccessing has been suspended. 88 | @type content: L{Object} 89 | @return: An element. 90 | @rtype: L{Element} 91 | """ 92 | return Element(content.tag) 93 | 94 | def start(self, content): 95 | """ 96 | Appending this content has started. 97 | @param content: The content for which proccessing has started. 98 | @type content: L{Content} 99 | @return: True to continue appending 100 | @rtype: boolean 101 | """ 102 | return True 103 | 104 | def suspend(self, content): 105 | """ 106 | Appending this content has suspended. 107 | @param content: The content for which proccessing has been suspended. 108 | @type content: L{Content} 109 | """ 110 | pass 111 | 112 | def resume(self, content): 113 | """ 114 | Appending this content has resumed. 115 | @param content: The content for which proccessing has been resumed. 116 | @type content: L{Content} 117 | """ 118 | pass 119 | 120 | def end(self, parent, content): 121 | """ 122 | Appending this content has ended. 123 | @param parent: The parent node ending. 124 | @type parent: L{Element} 125 | @param content: The content for which proccessing has ended. 126 | @type content: L{Content} 127 | """ 128 | pass 129 | 130 | def setnil(self, node, content): 131 | """ 132 | Set the value of the I{node} to nill. 133 | @param node: A I{nil} node. 134 | @type node: L{Element} 135 | @param content: The content to set nil. 136 | @type content: L{Content} 137 | """ 138 | pass 139 | 140 | def setdefault(self, node, content): 141 | """ 142 | Set the value of the I{node} to a default value. 143 | @param node: A I{nil} node. 144 | @type node: L{Element} 145 | @param content: The content to set the default value. 146 | @type content: L{Content} 147 | @return: The default. 148 | """ 149 | pass 150 | 151 | def optional(self, content): 152 | """ 153 | Get whether the specified content is optional. 154 | @param content: The content which to check. 155 | @type content: L{Content} 156 | """ 157 | return False 158 | 159 | -------------------------------------------------------------------------------- /bin/suds/mx/encoded.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides encoded I{marshaller} classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx import * 24 | from suds.mx.literal import Literal 25 | from suds.mx.typer import Typer 26 | from suds.sudsobject import Factory, Object 27 | from suds.xsd.query import TypeQuery 28 | 29 | log = getLogger(__name__) 30 | 31 | # 32 | # Add encoded extensions 33 | # aty = The soap (section 5) encoded array type. 34 | # 35 | Content.extensions.append('aty') 36 | 37 | 38 | class Encoded(Literal): 39 | """ 40 | A SOAP section (5) encoding marshaller. 41 | This marshaller supports rpc/encoded soap styles. 42 | """ 43 | 44 | def start(self, content): 45 | # 46 | # For soap encoded arrays, the 'aty' (array type) information 47 | # is extracted and added to the 'content'. Then, the content.value 48 | # is replaced with an object containing an 'item=[]' attribute 49 | # containing values that are 'typed' suds objects. 50 | # 51 | start = Literal.start(self, content) 52 | if start and isinstance(content.value, (list,tuple)): 53 | resolved = content.type.resolve() 54 | for c in resolved: 55 | if hasattr(c[0], 'aty'): 56 | content.aty = (content.tag, c[0].aty) 57 | self.cast(content) 58 | break 59 | return start 60 | 61 | def end(self, parent, content): 62 | # 63 | # For soap encoded arrays, the soapenc:arrayType attribute is 64 | # added with proper type and size information. 65 | # Eg: soapenc:arrayType="xs:int[3]" 66 | # 67 | Literal.end(self, parent, content) 68 | if content.aty is None: 69 | return 70 | tag, aty = content.aty 71 | ns0 = ('at0', aty[1]) 72 | ns1 = ('at1', 'http://schemas.xmlsoap.org/soap/encoding/') 73 | array = content.value.item 74 | child = parent.getChild(tag) 75 | child.addPrefix(ns0[0], ns0[1]) 76 | child.addPrefix(ns1[0], ns1[1]) 77 | name = '%s:arrayType' % ns1[0] 78 | value = '%s:%s[%d]' % (ns0[0], aty[0], len(array)) 79 | child.set(name, value) 80 | 81 | def encode(self, node, content): 82 | if content.type.any(): 83 | Typer.auto(node, content.value) 84 | return 85 | if content.real.any(): 86 | Typer.auto(node, content.value) 87 | return 88 | ns = None 89 | name = content.real.name 90 | if self.xstq: 91 | ns = content.real.namespace() 92 | Typer.manual(node, name, ns) 93 | 94 | def cast(self, content): 95 | """ 96 | Cast the I{untyped} list items found in content I{value}. 97 | Each items contained in the list is checked for XSD type information. 98 | Items (values) that are I{untyped}, are replaced with suds objects and 99 | type I{metadata} is added. 100 | @param content: The content holding the collection. 101 | @type content: L{Content} 102 | @return: self 103 | @rtype: L{Encoded} 104 | """ 105 | aty = content.aty[1] 106 | resolved = content.type.resolve() 107 | array = Factory.object(resolved.name) 108 | array.item = [] 109 | query = TypeQuery(aty) 110 | ref = query.execute(self.schema) 111 | if ref is None: 112 | raise TypeNotFound(qref) 113 | for x in content.value: 114 | if isinstance(x, (list, tuple)): 115 | array.item.append(x) 116 | continue 117 | if isinstance(x, Object): 118 | md = x.__metadata__ 119 | md.sxtype = ref 120 | array.item.append(x) 121 | continue 122 | if isinstance(x, dict): 123 | x = Factory.object(ref.name, x) 124 | md = x.__metadata__ 125 | md.sxtype = ref 126 | array.item.append(x) 127 | continue 128 | x = Factory.property(ref.name, x) 129 | md = x.__metadata__ 130 | md.sxtype = ref 131 | array.item.append(x) 132 | content.value = array 133 | return self 134 | -------------------------------------------------------------------------------- /bin/suds/mx/typer.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides sx typing classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx import * 24 | from suds.sax import Namespace as NS 25 | from suds.sax.text import Text 26 | 27 | log = getLogger(__name__) 28 | 29 | 30 | class Typer: 31 | """ 32 | Provides XML node typing as either automatic or manual. 33 | @cvar types: A dict of class to xs type mapping. 34 | @type types: dict 35 | """ 36 | 37 | types = { 38 | int : ('int', NS.xsdns), 39 | long : ('long', NS.xsdns), 40 | float : ('float', NS.xsdns), 41 | str : ('string', NS.xsdns), 42 | unicode : ('string', NS.xsdns), 43 | Text : ('string', NS.xsdns), 44 | bool : ('boolean', NS.xsdns), 45 | } 46 | 47 | @classmethod 48 | def auto(cls, node, value=None): 49 | """ 50 | Automatically set the node's xsi:type attribute based on either I{value}'s 51 | class or the class of the node's text. When I{value} is an unmapped class, 52 | the default type (xs:any) is set. 53 | @param node: An XML node 54 | @type node: L{sax.element.Element} 55 | @param value: An object that is or would be the node's text. 56 | @type value: I{any} 57 | @return: The specified node. 58 | @rtype: L{sax.element.Element} 59 | """ 60 | if value is None: 61 | value = node.getText() 62 | if isinstance(value, Object): 63 | known = cls.known(value) 64 | if known.name is None: 65 | return node 66 | tm = (known.name, known.namespace()) 67 | else: 68 | tm = cls.types.get(value.__class__, cls.types.get(str)) 69 | cls.manual(node, *tm) 70 | return node 71 | 72 | @classmethod 73 | def manual(cls, node, tval, ns=None): 74 | """ 75 | Set the node's xsi:type attribute based on either I{value}'s 76 | class or the class of the node's text. Then adds the referenced 77 | prefix(s) to the node's prefix mapping. 78 | @param node: An XML node 79 | @type node: L{sax.element.Element} 80 | @param tval: The name of the schema type. 81 | @type tval: str 82 | @param ns: The XML namespace of I{tval}. 83 | @type ns: (prefix, uri) 84 | @return: The specified node. 85 | @rtype: L{sax.element.Element} 86 | """ 87 | xta = ':'.join((NS.xsins[0], 'type')) 88 | node.addPrefix(NS.xsins[0], NS.xsins[1]) 89 | if ns is None: 90 | node.set(xta, tval) 91 | else: 92 | ns = cls.genprefix(node, ns) 93 | qname = ':'.join((ns[0], tval)) 94 | node.set(xta, qname) 95 | node.addPrefix(ns[0], ns[1]) 96 | return node 97 | 98 | @classmethod 99 | def genprefix(cls, node, ns): 100 | """ 101 | Generate a prefix. 102 | @param node: An XML node on which the prefix will be used. 103 | @type node: L{sax.element.Element} 104 | @param ns: A namespace needing an unique prefix. 105 | @type ns: (prefix, uri) 106 | @return: The I{ns} with a new prefix. 107 | """ 108 | for n in range(1, 1024): 109 | p = 'ns%d' % n 110 | u = node.resolvePrefix(p, default=None) 111 | if u is None or u == ns[1]: 112 | return (p, ns[1]) 113 | raise Exception('auto prefix, exhausted') 114 | 115 | @classmethod 116 | def known(cls, object): 117 | try: 118 | md = object.__metadata__ 119 | known = md.sxtype 120 | return known 121 | except: 122 | pass 123 | 124 | -------------------------------------------------------------------------------- /bin/suds/options.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Suds basic options classes. 19 | """ 20 | 21 | from suds.properties import * 22 | from suds.wsse import Security 23 | from suds.xsd.doctor import Doctor 24 | from suds.transport import Transport 25 | from suds.cache import Cache, NoCache 26 | 27 | 28 | class TpLinker(AutoLinker): 29 | """ 30 | Transport (auto) linker used to manage linkage between 31 | transport objects Properties and those Properties that contain them. 32 | """ 33 | 34 | def updated(self, properties, prev, next): 35 | if isinstance(prev, Transport): 36 | tp = Unskin(prev.options) 37 | properties.unlink(tp) 38 | if isinstance(next, Transport): 39 | tp = Unskin(next.options) 40 | properties.link(tp) 41 | 42 | 43 | class Options(Skin): 44 | """ 45 | Options: 46 | - B{cache} - The XML document cache. May be set (None) for no caching. 47 | - type: L{Cache} 48 | - default: L{NoCache} 49 | - B{faults} - Raise faults raised by server, 50 | else return tuple from service method invocation as (httpcode, object). 51 | - type: I{bool} 52 | - default: True 53 | - B{service} - The default service name. 54 | - type: I{str} 55 | - default: None 56 | - B{port} - The default service port name, not tcp port. 57 | - type: I{str} 58 | - default: None 59 | - B{location} - This overrides the service port address I{URL} defined 60 | in the WSDL. 61 | - type: I{str} 62 | - default: None 63 | - B{transport} - The message transport. 64 | - type: L{Transport} 65 | - default: None 66 | - B{soapheaders} - The soap headers to be included in the soap message. 67 | - type: I{any} 68 | - default: None 69 | - B{wsse} - The web services I{security} provider object. 70 | - type: L{Security} 71 | - default: None 72 | - B{doctor} - A schema I{doctor} object. 73 | - type: L{Doctor} 74 | - default: None 75 | - B{xstq} - The B{x}ml B{s}chema B{t}ype B{q}ualified flag indicates 76 | that the I{xsi:type} attribute values should be qualified by namespace. 77 | - type: I{bool} 78 | - default: True 79 | - B{prefixes} - Elements of the soap message should be qualified (when needed) 80 | using XML prefixes as opposed to xmlns="" syntax. 81 | - type: I{bool} 82 | - default: True 83 | - B{retxml} - Flag that causes the I{raw} soap envelope to be returned instead 84 | of the python object graph. 85 | - type: I{bool} 86 | - default: False 87 | - B{prettyxml} - Flag that causes I{pretty} xml to be rendered when generating 88 | the outbound soap envelope. 89 | - type: I{bool} 90 | - default: False 91 | - B{autoblend} - Flag that ensures that the schema(s) defined within the 92 | WSDL import each other. 93 | - type: I{bool} 94 | - default: False 95 | - B{cachingpolicy} - The caching policy. 96 | - type: I{int} 97 | - 0 = Cache XML documents. 98 | - 1 = Cache WSDL (pickled) object. 99 | - default: 0 100 | - B{plugins} - A plugin container. 101 | - type: I{list} 102 | """ 103 | def __init__(self, **kwargs): 104 | domain = __name__ 105 | definitions = [ 106 | Definition('cache', Cache, NoCache()), 107 | Definition('faults', bool, True), 108 | Definition('transport', Transport, None, TpLinker()), 109 | Definition('service', (int, basestring), None), 110 | Definition('port', (int, basestring), None), 111 | Definition('location', basestring, None), 112 | Definition('soapheaders', (), ()), 113 | Definition('wsse', Security, None), 114 | Definition('doctor', Doctor, None), 115 | Definition('xstq', bool, True), 116 | Definition('prefixes', bool, True), 117 | Definition('retxml', bool, False), 118 | Definition('prettyxml', bool, False), 119 | Definition('autoblend', bool, False), 120 | Definition('cachingpolicy', int, 0), 121 | Definition('plugins', (list, tuple), []), 122 | ] 123 | Skin.__init__(self, domain, definitions, kwargs) 124 | -------------------------------------------------------------------------------- /bin/suds/plugin.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The plugin module provides classes for implementation 19 | of suds plugins. 20 | """ 21 | 22 | from suds import * 23 | from logging import getLogger 24 | 25 | log = getLogger(__name__) 26 | 27 | 28 | class Context(object): 29 | """ 30 | Plugin context. 31 | """ 32 | pass 33 | 34 | 35 | class InitContext(Context): 36 | """ 37 | Init Context. 38 | @ivar wsdl: The wsdl. 39 | @type wsdl: L{wsdl.Definitions} 40 | """ 41 | pass 42 | 43 | 44 | class DocumentContext(Context): 45 | """ 46 | The XML document load context. 47 | @ivar url: The URL. 48 | @type url: str 49 | @ivar document: Either the XML text or the B{parsed} document root. 50 | @type document: (str|L{sax.element.Element}) 51 | """ 52 | pass 53 | 54 | 55 | class MessageContext(Context): 56 | """ 57 | The context for sending the soap envelope. 58 | @ivar envelope: The soap envelope to be sent. 59 | @type envelope: (str|L{sax.element.Element}) 60 | @ivar reply: The reply. 61 | @type reply: (str|L{sax.element.Element}|object) 62 | """ 63 | pass 64 | 65 | 66 | class Plugin: 67 | """ 68 | Plugin base. 69 | """ 70 | pass 71 | 72 | 73 | class InitPlugin(Plugin): 74 | """ 75 | The base class for suds I{init} plugins. 76 | """ 77 | 78 | def initialized(self, context): 79 | """ 80 | Suds client initialization. 81 | Called after wsdl the has been loaded. Provides the plugin 82 | with the opportunity to inspect/modify the WSDL. 83 | @param context: The init context. 84 | @type context: L{InitContext} 85 | """ 86 | pass 87 | 88 | 89 | class DocumentPlugin(Plugin): 90 | """ 91 | The base class for suds I{document} plugins. 92 | """ 93 | 94 | def loaded(self, context): 95 | """ 96 | Suds has loaded a WSDL/XSD document. Provides the plugin 97 | with an opportunity to inspect/modify the unparsed document. 98 | Called after each WSDL/XSD document is loaded. 99 | @param context: The document context. 100 | @type context: L{DocumentContext} 101 | """ 102 | pass 103 | 104 | def parsed(self, context): 105 | """ 106 | Suds has parsed a WSDL/XSD document. Provides the plugin 107 | with an opportunity to inspect/modify the parsed document. 108 | Called after each WSDL/XSD document is parsed. 109 | @param context: The document context. 110 | @type context: L{DocumentContext} 111 | """ 112 | pass 113 | 114 | 115 | class MessagePlugin(Plugin): 116 | """ 117 | The base class for suds I{soap message} plugins. 118 | """ 119 | 120 | def marshalled(self, context): 121 | """ 122 | Suds will send the specified soap envelope. 123 | Provides the plugin with the opportunity to inspect/modify 124 | the envelope Document before it is sent. 125 | @param context: The send context. 126 | The I{envelope} is the envelope docuemnt. 127 | @type context: L{MessageContext} 128 | """ 129 | pass 130 | 131 | def sending(self, context): 132 | """ 133 | Suds will send the specified soap envelope. 134 | Provides the plugin with the opportunity to inspect/modify 135 | the message text it is sent. 136 | @param context: The send context. 137 | The I{envelope} is the envelope text. 138 | @type context: L{MessageContext} 139 | """ 140 | pass 141 | 142 | def received(self, context): 143 | """ 144 | Suds has received the specified reply. 145 | Provides the plugin with the opportunity to inspect/modify 146 | the received XML text before it is SAX parsed. 147 | @param context: The reply context. 148 | The I{reply} is the raw text. 149 | @type context: L{MessageContext} 150 | """ 151 | pass 152 | 153 | def parsed(self, context): 154 | """ 155 | Suds has sax parsed the received reply. 156 | Provides the plugin with the opportunity to inspect/modify 157 | the sax parsed DOM tree for the reply before it is unmarshalled. 158 | @param context: The reply context. 159 | The I{reply} is DOM tree. 160 | @type context: L{MessageContext} 161 | """ 162 | pass 163 | 164 | def unmarshalled(self, context): 165 | """ 166 | Suds has unmarshalled the received reply. 167 | Provides the plugin with the opportunity to inspect/modify 168 | the unmarshalled reply object before it is returned. 169 | @param context: The reply context. 170 | The I{reply} is unmarshalled suds object. 171 | @type context: L{MessageContext} 172 | """ 173 | pass 174 | 175 | 176 | class PluginContainer: 177 | """ 178 | Plugin container provides easy method invocation. 179 | @ivar plugins: A list of plugin objects. 180 | @type plugins: [L{Plugin},] 181 | @cvar ctxclass: A dict of plugin method / context classes. 182 | @type ctxclass: dict 183 | """ 184 | 185 | domains = {\ 186 | 'init': (InitContext, InitPlugin), 187 | 'document': (DocumentContext, DocumentPlugin), 188 | 'message': (MessageContext, MessagePlugin ), 189 | } 190 | 191 | def __init__(self, plugins): 192 | """ 193 | @param plugins: A list of plugin objects. 194 | @type plugins: [L{Plugin},] 195 | """ 196 | self.plugins = plugins 197 | 198 | def __getattr__(self, name): 199 | domain = self.domains.get(name) 200 | if domain: 201 | plugins = [] 202 | ctx, pclass = domain 203 | for p in self.plugins: 204 | if isinstance(p, pclass): 205 | plugins.append(p) 206 | return PluginDomain(ctx, plugins) 207 | else: 208 | raise Exception, 'plugin domain (%s), invalid' % name 209 | 210 | 211 | class PluginDomain: 212 | """ 213 | The plugin domain. 214 | @ivar ctx: A context. 215 | @type ctx: L{Context} 216 | @ivar plugins: A list of plugins (targets). 217 | @type plugins: list 218 | """ 219 | 220 | def __init__(self, ctx, plugins): 221 | self.ctx = ctx 222 | self.plugins = plugins 223 | 224 | def __getattr__(self, name): 225 | return Method(name, self) 226 | 227 | 228 | class Method: 229 | """ 230 | Plugin method. 231 | @ivar name: The method name. 232 | @type name: str 233 | @ivar domain: The plugin domain. 234 | @type domain: L{PluginDomain} 235 | """ 236 | 237 | def __init__(self, name, domain): 238 | """ 239 | @param name: The method name. 240 | @type name: str 241 | @param domain: A plugin domain. 242 | @type domain: L{PluginDomain} 243 | """ 244 | self.name = name 245 | self.domain = domain 246 | 247 | def __call__(self, **kwargs): 248 | ctx = self.domain.ctx() 249 | ctx.__dict__.update(kwargs) 250 | for plugin in self.domain.plugins: 251 | try: 252 | method = getattr(plugin, self.name, None) 253 | if method and callable(method): 254 | method(ctx) 255 | except Exception, pe: 256 | log.exception(pe) 257 | return ctx 258 | -------------------------------------------------------------------------------- /bin/suds/reader.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains xml document reader classes. 19 | """ 20 | 21 | 22 | from suds.sax.parser import Parser 23 | from suds.transport import Request 24 | from suds.cache import Cache, NoCache 25 | from suds.store import DocumentStore 26 | from suds.plugin import PluginContainer 27 | from logging import getLogger 28 | 29 | 30 | log = getLogger(__name__) 31 | 32 | 33 | class Reader: 34 | """ 35 | The reader provides integration with cache. 36 | @ivar options: An options object. 37 | @type options: I{Options} 38 | """ 39 | 40 | def __init__(self, options): 41 | """ 42 | @param options: An options object. 43 | @type options: I{Options} 44 | """ 45 | self.options = options 46 | self.plugins = PluginContainer(options.plugins) 47 | 48 | def mangle(self, name, x): 49 | """ 50 | Mangle the name by hashing the I{name} and appending I{x}. 51 | @return: the mangled name. 52 | """ 53 | h = abs(hash(name)) 54 | return '%s-%s' % (h, x) 55 | 56 | 57 | class DocumentReader(Reader): 58 | """ 59 | The XML document reader provides an integration 60 | between the SAX L{Parser} and the document cache. 61 | """ 62 | 63 | def open(self, url): 64 | """ 65 | Open an XML document at the specified I{url}. 66 | First, the document attempted to be retrieved from 67 | the I{object cache}. If not found, it is downloaded and 68 | parsed using the SAX parser. The result is added to the 69 | cache for the next open(). 70 | @param url: A document url. 71 | @type url: str. 72 | @return: The specified XML document. 73 | @rtype: I{Document} 74 | """ 75 | cache = self.cache() 76 | id = self.mangle(url, 'document') 77 | d = cache.get(id) 78 | if d is None: 79 | d = self.download(url) 80 | cache.put(id, d) 81 | self.plugins.document.parsed(url=url, document=d.root()) 82 | return d 83 | 84 | def download(self, url): 85 | """ 86 | Download the docuemnt. 87 | @param url: A document url. 88 | @type url: str. 89 | @return: A file pointer to the docuemnt. 90 | @rtype: file-like 91 | """ 92 | store = DocumentStore() 93 | fp = store.open(url) 94 | if fp is None: 95 | fp = self.options.transport.open(Request(url)) 96 | content = fp.read() 97 | fp.close() 98 | ctx = self.plugins.document.loaded(url=url, document=content) 99 | content = ctx.document 100 | sax = Parser() 101 | return sax.parse(string=content) 102 | 103 | def cache(self): 104 | """ 105 | Get the cache. 106 | @return: The I{options} when I{cachingpolicy} = B{0}. 107 | @rtype: L{Cache} 108 | """ 109 | if self.options.cachingpolicy == 0: 110 | return self.options.cache 111 | else: 112 | return NoCache() 113 | 114 | 115 | class DefinitionsReader(Reader): 116 | """ 117 | The WSDL definitions reader provides an integration 118 | between the Definitions and the object cache. 119 | @ivar fn: A factory function (constructor) used to 120 | create the object not found in the cache. 121 | @type fn: I{Constructor} 122 | """ 123 | 124 | def __init__(self, options, fn): 125 | """ 126 | @param options: An options object. 127 | @type options: I{Options} 128 | @param fn: A factory function (constructor) used to 129 | create the object not found in the cache. 130 | @type fn: I{Constructor} 131 | """ 132 | Reader.__init__(self, options) 133 | self.fn = fn 134 | 135 | def open(self, url): 136 | """ 137 | Open a WSDL at the specified I{url}. 138 | First, the WSDL attempted to be retrieved from 139 | the I{object cache}. After unpickled from the cache, the 140 | I{options} attribute is restored. 141 | If not found, it is downloaded and instantiated using the 142 | I{fn} constructor and added to the cache for the next open(). 143 | @param url: A WSDL url. 144 | @type url: str. 145 | @return: The WSDL object. 146 | @rtype: I{Definitions} 147 | """ 148 | cache = self.cache() 149 | id = self.mangle(url, 'wsdl') 150 | d = cache.get(id) 151 | if d is None: 152 | d = self.fn(url, self.options) 153 | cache.put(id, d) 154 | else: 155 | d.options = self.options 156 | for imp in d.imports: 157 | imp.imported.options = self.options 158 | return d 159 | 160 | def cache(self): 161 | """ 162 | Get the cache. 163 | @return: The I{options} when I{cachingpolicy} = B{1}. 164 | @rtype: L{Cache} 165 | """ 166 | if self.options.cachingpolicy == 1: 167 | return self.options.cache 168 | else: 169 | return NoCache() -------------------------------------------------------------------------------- /bin/suds/sax/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The sax module contains a collection of classes that provide a 19 | (D)ocument (O)bject (M)odel representation of an XML document. 20 | The goal is to provide an easy, intuative interface for managing XML 21 | documents. Although, the term, DOM, is used above, this model is 22 | B{far} better. 23 | 24 | XML namespaces in suds are represented using a (2) element tuple 25 | containing the prefix and the URI. Eg: I{('tns', 'http://myns')} 26 | 27 | @var encoder: A I{pluggable} XML special character processor used to 28 | encode/decode strings. 29 | @type encoder: L{Encoder} 30 | """ 31 | 32 | from suds.sax.enc import Encoder 33 | 34 | # 35 | # pluggable XML special character encoder. 36 | # 37 | encoder = Encoder() 38 | 39 | 40 | def splitPrefix(name): 41 | """ 42 | Split the name into a tuple (I{prefix}, I{name}). The first element in 43 | the tuple is I{None} when the name does't have a prefix. 44 | @param name: A node name containing an optional prefix. 45 | @type name: basestring 46 | @return: A tuple containing the (2) parts of I{name} 47 | @rtype: (I{prefix}, I{name}) 48 | """ 49 | if isinstance(name, basestring) \ 50 | and ':' in name: 51 | return tuple(name.split(':', 1)) 52 | else: 53 | return (None, name) 54 | 55 | 56 | class Namespace: 57 | """ 58 | The namespace class represents XML namespaces. 59 | """ 60 | 61 | default = (None, None) 62 | xmlns = ('xml', 'http://www.w3.org/XML/1998/namespace') 63 | xsdns = ('xs', 'http://www.w3.org/2001/XMLSchema') 64 | xsins = ('xsi', 'http://www.w3.org/2001/XMLSchema-instance') 65 | all = (xsdns, xsins) 66 | 67 | @classmethod 68 | def create(cls, p=None, u=None): 69 | return (p, u) 70 | 71 | @classmethod 72 | def none(cls, ns): 73 | return ( ns == cls.default ) 74 | 75 | @classmethod 76 | def xsd(cls, ns): 77 | try: 78 | return cls.w3(ns) and ns[1].endswith('XMLSchema') 79 | except: 80 | pass 81 | return False 82 | 83 | @classmethod 84 | def xsi(cls, ns): 85 | try: 86 | return cls.w3(ns) and ns[1].endswith('XMLSchema-instance') 87 | except: 88 | pass 89 | return False 90 | 91 | @classmethod 92 | def xs(cls, ns): 93 | return ( cls.xsd(ns) or cls.xsi(ns) ) 94 | 95 | @classmethod 96 | def w3(cls, ns): 97 | try: 98 | return ns[1].startswith('http://www.w3.org') 99 | except: 100 | pass 101 | return False 102 | 103 | @classmethod 104 | def isns(cls, ns): 105 | try: 106 | return isinstance(ns, tuple) and len(ns) == len(cls.default) 107 | except: 108 | pass 109 | return False 110 | -------------------------------------------------------------------------------- /bin/suds/sax/attribute.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides XML I{attribute} classes. 19 | """ 20 | 21 | import suds.sax 22 | from logging import getLogger 23 | from suds import * 24 | from suds.sax import * 25 | from suds.sax.text import Text 26 | 27 | log = getLogger(__name__) 28 | 29 | class Attribute: 30 | """ 31 | An XML attribute object. 32 | @ivar parent: The node containing this attribute 33 | @type parent: L{element.Element} 34 | @ivar prefix: The I{optional} namespace prefix. 35 | @type prefix: basestring 36 | @ivar name: The I{unqualified} name of the attribute 37 | @type name: basestring 38 | @ivar value: The attribute's value 39 | @type value: basestring 40 | """ 41 | def __init__(self, name, value=None): 42 | """ 43 | @param name: The attribute's name with I{optional} namespace prefix. 44 | @type name: basestring 45 | @param value: The attribute's value 46 | @type value: basestring 47 | """ 48 | self.parent = None 49 | self.prefix, self.name = splitPrefix(name) 50 | self.setValue(value) 51 | 52 | def clone(self, parent=None): 53 | """ 54 | Clone this object. 55 | @param parent: The parent for the clone. 56 | @type parent: L{element.Element} 57 | @return: A copy of this object assigned to the new parent. 58 | @rtype: L{Attribute} 59 | """ 60 | a = Attribute(self.qname(), self.value) 61 | a.parent = parent 62 | return a 63 | 64 | def qname(self): 65 | """ 66 | Get the B{fully} qualified name of this attribute 67 | @return: The fully qualified name. 68 | @rtype: basestring 69 | """ 70 | if self.prefix is None: 71 | return self.name 72 | else: 73 | return ':'.join((self.prefix, self.name)) 74 | 75 | def setValue(self, value): 76 | """ 77 | Set the attributes value 78 | @param value: The new value (may be None) 79 | @type value: basestring 80 | @return: self 81 | @rtype: L{Attribute} 82 | """ 83 | if isinstance(value, Text): 84 | self.value = value 85 | else: 86 | self.value = Text(value) 87 | return self 88 | 89 | def getValue(self, default=Text('')): 90 | """ 91 | Get the attributes value with optional default. 92 | @param default: An optional value to be return when the 93 | attribute's has not been set. 94 | @type default: basestring 95 | @return: The attribute's value, or I{default} 96 | @rtype: L{Text} 97 | """ 98 | if self.hasText(): 99 | return self.value 100 | else: 101 | return default 102 | 103 | def hasText(self): 104 | """ 105 | Get whether the attribute has I{text} and that it is not an empty 106 | (zero length) string. 107 | @return: True when has I{text}. 108 | @rtype: boolean 109 | """ 110 | return ( self.value is not None and len(self.value) ) 111 | 112 | def namespace(self): 113 | """ 114 | Get the attributes namespace. This may either be the namespace 115 | defined by an optional prefix, or its parent's namespace. 116 | @return: The attribute's namespace 117 | @rtype: (I{prefix}, I{name}) 118 | """ 119 | if self.prefix is None: 120 | return Namespace.default 121 | else: 122 | return self.resolvePrefix(self.prefix) 123 | 124 | def resolvePrefix(self, prefix): 125 | """ 126 | Resolve the specified prefix to a known namespace. 127 | @param prefix: A declared prefix 128 | @type prefix: basestring 129 | @return: The namespace that has been mapped to I{prefix} 130 | @rtype: (I{prefix}, I{name}) 131 | """ 132 | ns = Namespace.default 133 | if self.parent is not None: 134 | ns = self.parent.resolvePrefix(prefix) 135 | return ns 136 | 137 | def match(self, name=None, ns=None): 138 | """ 139 | Match by (optional) name and/or (optional) namespace. 140 | @param name: The optional attribute tag name. 141 | @type name: str 142 | @param ns: An optional namespace. 143 | @type ns: (I{prefix}, I{name}) 144 | @return: True if matched. 145 | @rtype: boolean 146 | """ 147 | if name is None: 148 | byname = True 149 | else: 150 | byname = ( self.name == name ) 151 | if ns is None: 152 | byns = True 153 | else: 154 | byns = ( self.namespace()[1] == ns[1] ) 155 | return ( byname and byns ) 156 | 157 | def __eq__(self, rhs): 158 | """ equals operator """ 159 | return rhs is not None and \ 160 | isinstance(rhs, Attribute) and \ 161 | self.prefix == rhs.name and \ 162 | self.name == rhs.name 163 | 164 | def __repr__(self): 165 | """ get a string representation """ 166 | return \ 167 | 'attr (prefix=%s, name=%s, value=(%s))' %\ 168 | (self.prefix, self.name, self.value) 169 | 170 | def __str__(self): 171 | """ get an xml string representation """ 172 | return unicode(self).encode('utf-8') 173 | 174 | def __unicode__(self): 175 | """ get an xml string representation """ 176 | n = self.qname() 177 | if self.hasText(): 178 | v = self.value.escape() 179 | else: 180 | v = self.value 181 | return u'%s="%s"' % (n, v) 182 | -------------------------------------------------------------------------------- /bin/suds/sax/document.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides XML I{document} classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sax import * 24 | from suds.sax.element import Element 25 | 26 | log = getLogger(__name__) 27 | 28 | class Document(Element): 29 | """ simple document """ 30 | 31 | DECL = '' 32 | 33 | def __init__(self, root=None): 34 | Element.__init__(self, 'document') 35 | if root is not None: 36 | self.append(root) 37 | 38 | def root(self): 39 | if len(self.children): 40 | return self.children[0] 41 | else: 42 | return None 43 | 44 | def str(self): 45 | s = [] 46 | s.append(self.DECL) 47 | s.append('\n') 48 | s.append(self.root().str()) 49 | return ''.join(s) 50 | 51 | def plain(self): 52 | s = [] 53 | s.append(self.DECL) 54 | s.append(self.root().plain()) 55 | return ''.join(s) 56 | 57 | def __str__(self): 58 | return unicode(self).encode('utf-8') 59 | 60 | def __unicode__(self): 61 | return self.str() -------------------------------------------------------------------------------- /bin/suds/sax/enc.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides XML I{special character} encoder classes. 19 | """ 20 | 21 | import re 22 | 23 | class Encoder: 24 | """ 25 | An XML special character encoder/decoder. 26 | @cvar encodings: A mapping of special characters encoding. 27 | @type encodings: [(str,str)] 28 | @cvar decodings: A mapping of special characters decoding. 29 | @type decodings: [(str,str)] 30 | @cvar special: A list of special characters 31 | @type special: [char] 32 | """ 33 | 34 | encodings = \ 35 | (( '&(?!(amp|lt|gt|quot|apos);)', '&' ),( '<', '<' ),( '>', '>' ),( '"', '"' ),("'", ''' )) 36 | decodings = \ 37 | (( '<', '<' ),( '>', '>' ),( '"', '"' ),( ''', "'" ),( '&', '&' )) 38 | special = \ 39 | ('&', '<', '>', '"', "'") 40 | 41 | def needsEncoding(self, s): 42 | """ 43 | Get whether string I{s} contains special characters. 44 | @param s: A string to check. 45 | @type s: str 46 | @return: True if needs encoding. 47 | @rtype: boolean 48 | """ 49 | if isinstance(s, basestring): 50 | for c in self.special: 51 | if c in s: 52 | return True 53 | return False 54 | 55 | def encode(self, s): 56 | """ 57 | Encode special characters found in string I{s}. 58 | @param s: A string to encode. 59 | @type s: str 60 | @return: The encoded string. 61 | @rtype: str 62 | """ 63 | if isinstance(s, basestring) and self.needsEncoding(s): 64 | for x in self.encodings: 65 | s = re.sub(x[0], x[1], s) 66 | return s 67 | 68 | def decode(self, s): 69 | """ 70 | Decode special characters encodings found in string I{s}. 71 | @param s: A string to decode. 72 | @type s: str 73 | @return: The decoded string. 74 | @rtype: str 75 | """ 76 | if isinstance(s, basestring) and '&' in s: 77 | for x in self.decodings: 78 | s = s.replace(x[0], x[1]) 79 | return s 80 | -------------------------------------------------------------------------------- /bin/suds/sax/parser.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The sax module contains a collection of classes that provide a 19 | (D)ocument (O)bject (M)odel representation of an XML document. 20 | The goal is to provide an easy, intuative interface for managing XML 21 | documents. Although, the term, DOM, is used above, this model is 22 | B{far} better. 23 | 24 | XML namespaces in suds are represented using a (2) element tuple 25 | containing the prefix and the URI. Eg: I{('tns', 'http://myns')} 26 | 27 | """ 28 | 29 | from logging import getLogger 30 | import suds.metrics 31 | from suds import * 32 | from suds.sax import * 33 | from suds.sax.document import Document 34 | from suds.sax.element import Element 35 | from suds.sax.text import Text 36 | from suds.sax.attribute import Attribute 37 | from xml.sax import make_parser, InputSource, ContentHandler 38 | from xml.sax.handler import feature_external_ges 39 | from cStringIO import StringIO 40 | 41 | log = getLogger(__name__) 42 | 43 | 44 | class Handler(ContentHandler): 45 | """ sax hanlder """ 46 | 47 | def __init__(self): 48 | self.nodes = [Document()] 49 | 50 | def startElement(self, name, attrs): 51 | top = self.top() 52 | node = Element(unicode(name), parent=top) 53 | for a in attrs.getNames(): 54 | n = unicode(a) 55 | v = unicode(attrs.getValue(a)) 56 | attribute = Attribute(n,v) 57 | if self.mapPrefix(node, attribute): 58 | continue 59 | node.append(attribute) 60 | node.charbuffer = [] 61 | top.append(node) 62 | self.push(node) 63 | 64 | def mapPrefix(self, node, attribute): 65 | skip = False 66 | if attribute.name == 'xmlns': 67 | if len(attribute.value): 68 | node.expns = unicode(attribute.value) 69 | skip = True 70 | elif attribute.prefix == 'xmlns': 71 | prefix = attribute.name 72 | node.nsprefixes[prefix] = unicode(attribute.value) 73 | skip = True 74 | return skip 75 | 76 | def endElement(self, name): 77 | name = unicode(name) 78 | current = self.top() 79 | if len(current.charbuffer): 80 | current.text = Text(u''.join(current.charbuffer)) 81 | del current.charbuffer 82 | if len(current): 83 | current.trim() 84 | currentqname = current.qname() 85 | if name == currentqname: 86 | self.pop() 87 | else: 88 | raise Exception('malformed document') 89 | 90 | def characters(self, content): 91 | text = unicode(content) 92 | node = self.top() 93 | node.charbuffer.append(text) 94 | 95 | def push(self, node): 96 | self.nodes.append(node) 97 | return node 98 | 99 | def pop(self): 100 | return self.nodes.pop() 101 | 102 | def top(self): 103 | return self.nodes[len(self.nodes)-1] 104 | 105 | 106 | class Parser: 107 | """ SAX Parser """ 108 | 109 | @classmethod 110 | def saxparser(cls): 111 | p = make_parser() 112 | p.setFeature(feature_external_ges, 0) 113 | h = Handler() 114 | p.setContentHandler(h) 115 | return (p, h) 116 | 117 | def parse(self, file=None, string=None): 118 | """ 119 | SAX parse XML text. 120 | @param file: Parse a python I{file-like} object. 121 | @type file: I{file-like} object. 122 | @param string: Parse string XML. 123 | @type string: str 124 | """ 125 | timer = metrics.Timer() 126 | timer.start() 127 | sax, handler = self.saxparser() 128 | if file is not None: 129 | sax.parse(file) 130 | timer.stop() 131 | metrics.log.debug('sax (%s) duration: %s', file, timer) 132 | return handler.nodes[0] 133 | if string is not None: 134 | source = InputSource(None) 135 | source.setByteStream(StringIO(string)) 136 | sax.parse(source) 137 | timer.stop() 138 | metrics.log.debug('%s\nsax duration: %s', string, timer) 139 | return handler.nodes[0] -------------------------------------------------------------------------------- /bin/suds/sax/text.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains XML text classes. 19 | """ 20 | 21 | from suds import * 22 | from suds.sax import * 23 | 24 | 25 | class Text(unicode): 26 | """ 27 | An XML text object used to represent text content. 28 | @ivar lang: The (optional) language flag. 29 | @type lang: bool 30 | @ivar escaped: The (optional) XML special character escaped flag. 31 | @type escaped: bool 32 | """ 33 | __slots__ = ('lang', 'escaped',) 34 | 35 | @classmethod 36 | def __valid(cls, *args): 37 | return ( len(args) and args[0] is not None ) 38 | 39 | def __new__(cls, *args, **kwargs): 40 | if cls.__valid(*args): 41 | lang = kwargs.pop('lang', None) 42 | escaped = kwargs.pop('escaped', False) 43 | result = super(Text, cls).__new__(cls, *args, **kwargs) 44 | result.lang = lang 45 | result.escaped = escaped 46 | else: 47 | result = None 48 | return result 49 | 50 | def escape(self): 51 | """ 52 | Encode (escape) special XML characters. 53 | @return: The text with XML special characters escaped. 54 | @rtype: L{Text} 55 | """ 56 | if not self.escaped: 57 | post = sax.encoder.encode(self) 58 | escaped = ( post != self ) 59 | return Text(post, lang=self.lang, escaped=escaped) 60 | return self 61 | 62 | def unescape(self): 63 | """ 64 | Decode (unescape) special XML characters. 65 | @return: The text with escaped XML special characters decoded. 66 | @rtype: L{Text} 67 | """ 68 | if self.escaped: 69 | post = sax.encoder.decode(self) 70 | return Text(post, lang=self.lang) 71 | return self 72 | 73 | def trim(self): 74 | post = self.strip() 75 | return Text(post, lang=self.lang, escaped=self.escaped) 76 | 77 | def __add__(self, other): 78 | joined = u''.join((self, other)) 79 | result = Text(joined, lang=self.lang, escaped=self.escaped) 80 | if isinstance(other, Text): 81 | result.escaped = ( self.escaped or other.escaped ) 82 | return result 83 | 84 | def __repr__(self): 85 | s = [self] 86 | if self.lang is not None: 87 | s.append(' [%s]' % self.lang) 88 | if self.escaped: 89 | s.append(' ') 90 | return ''.join(s) 91 | 92 | def __getstate__(self): 93 | state = {} 94 | for k in self.__slots__: 95 | state[k] = getattr(self, k) 96 | return state 97 | 98 | def __setstate__(self, state): 99 | for k in self.__slots__: 100 | setattr(self, k, state[k]) 101 | 102 | 103 | class Raw(Text): 104 | """ 105 | Raw text which is not XML escaped. 106 | This may include I{string} XML. 107 | """ 108 | def escape(self): 109 | return self 110 | 111 | def unescape(self): 112 | return self 113 | 114 | def __add__(self, other): 115 | joined = u''.join((self, other)) 116 | return Raw(joined, lang=self.lang) 117 | -------------------------------------------------------------------------------- /bin/suds/servicedefinition.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{service definition} provides a textual representation of a service. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | import suds.metrics as metrics 24 | from suds.sax import Namespace 25 | 26 | log = getLogger(__name__) 27 | 28 | class ServiceDefinition: 29 | """ 30 | A service definition provides an object used to generate a textual description 31 | of a service. 32 | @ivar wsdl: A wsdl. 33 | @type wsdl: L{wsdl.Definitions} 34 | @ivar service: The service object. 35 | @type service: L{suds.wsdl.Service} 36 | @ivar ports: A list of port-tuple: (port, [(method-name, pdef)]) 37 | @type ports: [port-tuple,..] 38 | @ivar prefixes: A list of remapped prefixes. 39 | @type prefixes: [(prefix,uri),..] 40 | @ivar types: A list of type definitions 41 | @type types: [I{Type},..] 42 | """ 43 | 44 | def __init__(self, wsdl, service): 45 | """ 46 | @param wsdl: A wsdl object 47 | @type wsdl: L{Definitions} 48 | @param service: A service B{name}. 49 | @type service: str 50 | """ 51 | self.wsdl = wsdl 52 | self.service = service 53 | self.ports = [] 54 | self.params = [] 55 | self.types = [] 56 | self.prefixes = [] 57 | self.addports() 58 | self.paramtypes() 59 | self.publictypes() 60 | self.getprefixes() 61 | self.pushprefixes() 62 | 63 | def pushprefixes(self): 64 | """ 65 | Add our prefixes to the wsdl so that when users invoke methods 66 | and reference the prefixes, the will resolve properly. 67 | """ 68 | for ns in self.prefixes: 69 | self.wsdl.root.addPrefix(ns[0], ns[1]) 70 | 71 | def addports(self): 72 | """ 73 | Look through the list of service ports and construct a list of tuples where 74 | each tuple is used to describe a port and it's list of methods as: 75 | (port, [method]). Each method is tuple: (name, [pdef,..] where each pdef is 76 | a tuple: (param-name, type). 77 | """ 78 | timer = metrics.Timer() 79 | timer.start() 80 | for port in self.service.ports: 81 | p = self.findport(port) 82 | for op in port.binding.operations.values(): 83 | m = p[0].method(op.name) 84 | binding = m.binding.input 85 | method = (m.name, binding.param_defs(m)) 86 | p[1].append(method) 87 | metrics.log.debug("method '%s' created: %s", m.name, timer) 88 | p[1].sort() 89 | timer.stop() 90 | 91 | def findport(self, port): 92 | """ 93 | Find and return a port tuple for the specified port. 94 | Created and added when not found. 95 | @param port: A port. 96 | @type port: I{service.Port} 97 | @return: A port tuple. 98 | @rtype: (port, [method]) 99 | """ 100 | for p in self.ports: 101 | if p[0] == p: return p 102 | p = (port, []) 103 | self.ports.append(p) 104 | return p 105 | 106 | def getprefixes(self): 107 | """ 108 | Add prefixes foreach namespace referenced by parameter types. 109 | """ 110 | namespaces = [] 111 | for l in (self.params, self.types): 112 | for t,r in l: 113 | ns = r.namespace() 114 | if ns[1] is None: continue 115 | if ns[1] in namespaces: continue 116 | if Namespace.xs(ns) or Namespace.xsd(ns): 117 | continue 118 | namespaces.append(ns[1]) 119 | if t == r: continue 120 | ns = t.namespace() 121 | if ns[1] is None: continue 122 | if ns[1] in namespaces: continue 123 | namespaces.append(ns[1]) 124 | i = 0 125 | namespaces.sort() 126 | for u in namespaces: 127 | p = self.nextprefix() 128 | ns = (p, u) 129 | self.prefixes.append(ns) 130 | 131 | def paramtypes(self): 132 | """ get all parameter types """ 133 | for m in [p[1] for p in self.ports]: 134 | for p in [p[1] for p in m]: 135 | for pd in p: 136 | if pd[1] in self.params: continue 137 | item = (pd[1], pd[1].resolve()) 138 | self.params.append(item) 139 | 140 | def publictypes(self): 141 | """ get all public types """ 142 | for t in self.wsdl.schema.types.values(): 143 | if t in self.params: continue 144 | if t in self.types: continue 145 | item = (t, t) 146 | self.types.append(item) 147 | tc = lambda x,y: cmp(x[0].name, y[0].name) 148 | self.types.sort(cmp=tc) 149 | 150 | def nextprefix(self): 151 | """ 152 | Get the next available prefix. This means a prefix starting with 'ns' with 153 | a number appended as (ns0, ns1, ..) that is not already defined on the 154 | wsdl document. 155 | """ 156 | used = [ns[0] for ns in self.prefixes] 157 | used += [ns[0] for ns in self.wsdl.root.nsprefixes.items()] 158 | for n in range(0,1024): 159 | p = 'ns%d'%n 160 | if p not in used: 161 | return p 162 | raise Exception('prefixes exhausted') 163 | 164 | def getprefix(self, u): 165 | """ 166 | Get the prefix for the specified namespace (uri) 167 | @param u: A namespace uri. 168 | @type u: str 169 | @return: The namspace. 170 | @rtype: (prefix, uri). 171 | """ 172 | for ns in Namespace.all: 173 | if u == ns[1]: return ns[0] 174 | for ns in self.prefixes: 175 | if u == ns[1]: return ns[0] 176 | raise Exception('ns (%s) not mapped' % u) 177 | 178 | def xlate(self, type): 179 | """ 180 | Get a (namespace) translated I{qualified} name for specified type. 181 | @param type: A schema type. 182 | @type type: I{suds.xsd.sxbasic.SchemaObject} 183 | @return: A translated I{qualified} name. 184 | @rtype: str 185 | """ 186 | resolved = type.resolve() 187 | name = resolved.name 188 | if type.unbounded(): 189 | name += '[]' 190 | ns = resolved.namespace() 191 | if ns[1] == self.wsdl.tns[1]: 192 | return name 193 | prefix = self.getprefix(ns[1]) 194 | return ':'.join((prefix, name)) 195 | 196 | def description(self): 197 | """ 198 | Get a textual description of the service for which this object represents. 199 | @return: A textual description. 200 | @rtype: str 201 | """ 202 | s = [] 203 | indent = (lambda n : '\n%*s'%(n*3,' ')) 204 | s.append('Service ( %s ) tns="%s"' % (self.service.name, self.wsdl.tns[1])) 205 | s.append(indent(1)) 206 | s.append('Prefixes (%d)' % len(self.prefixes)) 207 | for p in self.prefixes: 208 | s.append(indent(2)) 209 | s.append('%s = "%s"' % p) 210 | s.append(indent(1)) 211 | s.append('Ports (%d):' % len(self.ports)) 212 | for p in self.ports: 213 | s.append(indent(2)) 214 | s.append('(%s)' % p[0].name) 215 | s.append(indent(3)) 216 | s.append('Methods (%d):' % len(p[1])) 217 | for m in p[1]: 218 | sig = [] 219 | s.append(indent(4)) 220 | sig.append(m[0]) 221 | sig.append('(') 222 | for p in m[1]: 223 | sig.append(self.xlate(p[1])) 224 | sig.append(' ') 225 | sig.append(p[0]) 226 | sig.append(', ') 227 | sig.append(')') 228 | try: 229 | s.append(''.join(sig)) 230 | except: 231 | pass 232 | s.append(indent(3)) 233 | s.append('Types (%d):' % len(self.types)) 234 | for t in self.types: 235 | s.append(indent(4)) 236 | s.append(self.xlate(t[0])) 237 | s.append('\n\n') 238 | return ''.join(s) 239 | 240 | def __str__(self): 241 | return unicode(self).encode('utf-8') 242 | 243 | def __unicode__(self): 244 | try: 245 | return self.description() 246 | except Exception, e: 247 | log.exception(e) 248 | return tostr(e) -------------------------------------------------------------------------------- /bin/suds/serviceproxy.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The service proxy provides access to web services. 19 | 20 | Replaced by: L{client.Client} 21 | """ 22 | 23 | from logging import getLogger 24 | from suds import * 25 | from suds.client import Client 26 | 27 | log = getLogger(__name__) 28 | 29 | 30 | class ServiceProxy(object): 31 | 32 | """ 33 | A lightweight soap based web service proxy. 34 | @ivar __client__: A client. 35 | Everything is delegated to the 2nd generation API. 36 | @type __client__: L{Client} 37 | @note: Deprecated, replaced by L{Client}. 38 | """ 39 | 40 | def __init__(self, url, **kwargs): 41 | """ 42 | @param url: The URL for the WSDL. 43 | @type url: str 44 | @param kwargs: keyword arguments. 45 | @keyword faults: Raise faults raised by server (default:True), 46 | else return tuple from service method invocation as (http code, object). 47 | @type faults: boolean 48 | @keyword proxy: An http proxy to be specified on requests (default:{}). 49 | The proxy is defined as {protocol:proxy,} 50 | @type proxy: dict 51 | """ 52 | client = Client(url, **kwargs) 53 | self.__client__ = client 54 | 55 | def get_instance(self, name): 56 | """ 57 | Get an instance of a WSDL type by name 58 | @param name: The name of a type defined in the WSDL. 59 | @type name: str 60 | @return: An instance on success, else None 61 | @rtype: L{sudsobject.Object} 62 | """ 63 | return self.__client__.factory.create(name) 64 | 65 | def get_enum(self, name): 66 | """ 67 | Get an instance of an enumeration defined in the WSDL by name. 68 | @param name: The name of a enumeration defined in the WSDL. 69 | @type name: str 70 | @return: An instance on success, else None 71 | @rtype: L{sudsobject.Object} 72 | """ 73 | return self.__client__.factory.create(name) 74 | 75 | def __str__(self): 76 | return str(self.__client__) 77 | 78 | def __unicode__(self): 79 | return unicode(self.__client__) 80 | 81 | def __getattr__(self, name): 82 | builtin = name.startswith('__') and name.endswith('__') 83 | if builtin: 84 | return self.__dict__[name] 85 | else: 86 | return getattr(self.__client__.service, name) -------------------------------------------------------------------------------- /bin/suds/soaparray.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{soaparray} module provides XSD extensions for handling 19 | soap (section 5) encoded arrays. 20 | """ 21 | 22 | from suds import * 23 | from logging import getLogger 24 | from suds.xsd.sxbasic import Factory as SXFactory 25 | from suds.xsd.sxbasic import Attribute as SXAttribute 26 | 27 | 28 | class Attribute(SXAttribute): 29 | """ 30 | Represents an XSD that handles special 31 | attributes that are extensions for WSDLs. 32 | @ivar aty: Array type information. 33 | @type aty: The value of wsdl:arrayType. 34 | """ 35 | 36 | def __init__(self, schema, root, aty): 37 | """ 38 | @param aty: Array type information. 39 | @type aty: The value of wsdl:arrayType. 40 | """ 41 | SXAttribute.__init__(self, schema, root) 42 | if aty.endswith('[]'): 43 | self.aty = aty[:-2] 44 | else: 45 | self.aty = aty 46 | 47 | def autoqualified(self): 48 | aqs = SXAttribute.autoqualified(self) 49 | aqs.append('aty') 50 | return aqs 51 | 52 | def description(self): 53 | d = SXAttribute.description(self) 54 | d = d+('aty',) 55 | return d 56 | 57 | # 58 | # Builder function, only builds Attribute when arrayType 59 | # attribute is defined on root. 60 | # 61 | def __fn(x, y): 62 | ns = (None, "http://schemas.xmlsoap.org/wsdl/") 63 | aty = y.get('arrayType', ns=ns) 64 | if aty is None: 65 | return SXAttribute(x, y) 66 | else: 67 | return Attribute(x, y, aty) 68 | 69 | # 70 | # Remap tags to __fn() builder. 71 | # 72 | SXFactory.maptag('attribute', __fn) -------------------------------------------------------------------------------- /bin/suds/transport/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains transport interface (classes). 19 | """ 20 | 21 | 22 | class TransportError(Exception): 23 | def __init__(self, reason, httpcode, fp=None): 24 | Exception.__init__(self, reason) 25 | self.httpcode = httpcode 26 | self.fp = fp 27 | 28 | class Request: 29 | """ 30 | A transport request 31 | @ivar url: The url for the request. 32 | @type url: str 33 | @ivar message: The message to be sent in a POST request. 34 | @type message: str 35 | @ivar headers: The http headers to be used for the request. 36 | @type headers: dict 37 | """ 38 | 39 | def __init__(self, url, message=None): 40 | """ 41 | @param url: The url for the request. 42 | @type url: str 43 | @param message: The (optional) message to be send in the request. 44 | @type message: str 45 | """ 46 | self.url = url 47 | self.headers = {} 48 | self.message = message 49 | 50 | def __str__(self): 51 | s = [] 52 | s.append('URL:%s' % self.url) 53 | s.append('HEADERS: %s' % self.headers) 54 | s.append('MESSAGE:') 55 | s.append(self.message) 56 | return '\n'.join(s) 57 | 58 | 59 | class Reply: 60 | """ 61 | A transport reply 62 | @ivar code: The http code returned. 63 | @type code: int 64 | @ivar message: The message to be sent in a POST request. 65 | @type message: str 66 | @ivar headers: The http headers to be used for the request. 67 | @type headers: dict 68 | """ 69 | 70 | def __init__(self, code, headers, message): 71 | """ 72 | @param code: The http code returned. 73 | @type code: int 74 | @param headers: The http returned headers. 75 | @type headers: dict 76 | @param message: The (optional) reply message received. 77 | @type message: str 78 | """ 79 | self.code = code 80 | self.headers = headers 81 | self.message = message 82 | 83 | def __str__(self): 84 | s = [] 85 | s.append('CODE: %s' % self.code) 86 | s.append('HEADERS: %s' % self.headers) 87 | s.append('MESSAGE:') 88 | s.append(self.message) 89 | return '\n'.join(s) 90 | 91 | 92 | class Transport: 93 | """ 94 | The transport I{interface}. 95 | """ 96 | 97 | def __init__(self): 98 | """ 99 | Constructor. 100 | """ 101 | from suds.transport.options import Options 102 | self.options = Options() 103 | del Options 104 | 105 | def open(self, request): 106 | """ 107 | Open the url in the specified request. 108 | @param request: A transport request. 109 | @type request: L{Request} 110 | @return: An input stream. 111 | @rtype: stream 112 | @raise TransportError: On all transport errors. 113 | """ 114 | raise Exception('not-implemented') 115 | 116 | def send(self, request): 117 | """ 118 | Send soap message. Implementations are expected to handle: 119 | - proxies 120 | - I{http} headers 121 | - cookies 122 | - sending message 123 | - brokering exceptions into L{TransportError} 124 | @param request: A transport request. 125 | @type request: L{Request} 126 | @return: The reply 127 | @rtype: L{Reply} 128 | @raise TransportError: On all transport errors. 129 | """ 130 | raise Exception('not-implemented') 131 | -------------------------------------------------------------------------------- /bin/suds/transport/http.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains classes for basic HTTP transport implementations. 19 | """ 20 | 21 | import urllib2 as u2 22 | import base64 23 | import socket 24 | from suds.transport import * 25 | from suds.properties import Unskin 26 | from urlparse import urlparse 27 | from cookielib import CookieJar 28 | from logging import getLogger 29 | 30 | log = getLogger(__name__) 31 | 32 | 33 | class HttpTransport(Transport): 34 | """ 35 | HTTP transport using urllib2. Provided basic http transport 36 | that provides for cookies, proxies but no authentication. 37 | """ 38 | 39 | def __init__(self, **kwargs): 40 | """ 41 | @param kwargs: Keyword arguments. 42 | - B{proxy} - An http proxy to be specified on requests. 43 | The proxy is defined as {protocol:proxy,} 44 | - type: I{dict} 45 | - default: {} 46 | - B{timeout} - Set the url open timeout (seconds). 47 | - type: I{float} 48 | - default: 90 49 | """ 50 | Transport.__init__(self) 51 | Unskin(self.options).update(kwargs) 52 | self.cookiejar = CookieJar() 53 | self.proxy = {} 54 | self.urlopener = None 55 | 56 | def open(self, request): 57 | try: 58 | url = request.url 59 | log.debug('opening (%s)', url) 60 | u2request = u2.Request(url) 61 | self.proxy = self.options.proxy 62 | return self.u2open(u2request) 63 | except u2.HTTPError, e: 64 | raise TransportError(str(e), e.code, e.fp) 65 | 66 | def send(self, request): 67 | result = None 68 | url = request.url 69 | msg = request.message 70 | headers = request.headers 71 | try: 72 | u2request = u2.Request(url, msg, headers) 73 | self.addcookies(u2request) 74 | self.proxy = self.options.proxy 75 | request.headers.update(u2request.headers) 76 | log.debug('sending:\n%s', request) 77 | fp = self.u2open(u2request) 78 | self.getcookies(fp, u2request) 79 | result = Reply(200, fp.headers.dict, fp.read()) 80 | log.debug('received:\n%s', result) 81 | except u2.HTTPError, e: 82 | if e.code in (202,204): 83 | result = None 84 | else: 85 | raise TransportError(e.msg, e.code, e.fp) 86 | return result 87 | 88 | def addcookies(self, u2request): 89 | """ 90 | Add cookies in the cookiejar to the request. 91 | @param u2request: A urllib2 request. 92 | @rtype: u2request: urllib2.Requet. 93 | """ 94 | self.cookiejar.add_cookie_header(u2request) 95 | 96 | def getcookies(self, fp, u2request): 97 | """ 98 | Add cookies in the request to the cookiejar. 99 | @param u2request: A urllib2 request. 100 | @rtype: u2request: urllib2.Requet. 101 | """ 102 | self.cookiejar.extract_cookies(fp, u2request) 103 | 104 | def u2open(self, u2request): 105 | """ 106 | Open a connection. 107 | @param u2request: A urllib2 request. 108 | @type u2request: urllib2.Requet. 109 | @return: The opened file-like urllib2 object. 110 | @rtype: fp 111 | """ 112 | tm = self.options.timeout 113 | url = self.u2opener() 114 | if self.u2ver() < 2.6: 115 | socket.setdefaulttimeout(tm) 116 | return url.open(u2request) 117 | else: 118 | return url.open(u2request, timeout=tm) 119 | 120 | def u2opener(self): 121 | """ 122 | Create a urllib opener. 123 | @return: An opener. 124 | @rtype: I{OpenerDirector} 125 | """ 126 | if self.urlopener is None: 127 | return u2.build_opener(*self.u2handlers()) 128 | else: 129 | return self.urlopener 130 | 131 | def u2handlers(self): 132 | """ 133 | Get a collection of urllib handlers. 134 | @return: A list of handlers to be installed in the opener. 135 | @rtype: [Handler,...] 136 | """ 137 | handlers = [] 138 | handlers.append(u2.ProxyHandler(self.proxy)) 139 | return handlers 140 | 141 | def u2ver(self): 142 | """ 143 | Get the major/minor version of the urllib2 lib. 144 | @return: The urllib2 version. 145 | @rtype: float 146 | """ 147 | try: 148 | part = u2.__version__.split('.', 1) 149 | n = float('.'.join(part)) 150 | return n 151 | except Exception, e: 152 | log.exception(e) 153 | return 0 154 | 155 | def __deepcopy__(self, memo={}): 156 | clone = self.__class__() 157 | p = Unskin(self.options) 158 | cp = Unskin(clone.options) 159 | cp.update(p) 160 | return clone 161 | 162 | 163 | class HttpAuthenticated(HttpTransport): 164 | """ 165 | Provides basic http authentication for servers that don't follow 166 | the specified challenge / response model. This implementation 167 | appends the I{Authorization} http header with base64 encoded 168 | credentials on every http request. 169 | """ 170 | 171 | def open(self, request): 172 | self.addcredentials(request) 173 | return HttpTransport.open(self, request) 174 | 175 | def send(self, request): 176 | self.addcredentials(request) 177 | return HttpTransport.send(self, request) 178 | 179 | def addcredentials(self, request): 180 | credentials = self.credentials() 181 | if not (None in credentials): 182 | encoded = base64.encodestring(':'.join(credentials)) 183 | basic = 'Basic %s' % encoded[:-1] 184 | request.headers['Authorization'] = basic 185 | 186 | def credentials(self): 187 | return (self.options.username, self.options.password) -------------------------------------------------------------------------------- /bin/suds/transport/https.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains classes for basic HTTP (authenticated) transport implementations. 19 | """ 20 | 21 | import urllib2 as u2 22 | from suds.transport import * 23 | from suds.transport.http import HttpTransport 24 | from logging import getLogger 25 | 26 | log = getLogger(__name__) 27 | 28 | 29 | class HttpAuthenticated(HttpTransport): 30 | """ 31 | Provides basic http authentication that follows the RFC-2617 specification. 32 | As defined by specifications, credentials are provided to the server 33 | upon request (HTTP/1.0 401 Authorization Required) by the server only. 34 | @ivar pm: The password manager. 35 | @ivar handler: The authentication handler. 36 | """ 37 | 38 | def __init__(self, **kwargs): 39 | """ 40 | @param kwargs: Keyword arguments. 41 | - B{proxy} - An http proxy to be specified on requests. 42 | The proxy is defined as {protocol:proxy,} 43 | - type: I{dict} 44 | - default: {} 45 | - B{timeout} - Set the url open timeout (seconds). 46 | - type: I{float} 47 | - default: 90 48 | - B{username} - The username used for http authentication. 49 | - type: I{str} 50 | - default: None 51 | - B{password} - The password used for http authentication. 52 | - type: I{str} 53 | - default: None 54 | """ 55 | HttpTransport.__init__(self, **kwargs) 56 | self.pm = u2.HTTPPasswordMgrWithDefaultRealm() 57 | 58 | def open(self, request): 59 | self.addcredentials(request) 60 | return HttpTransport.open(self, request) 61 | 62 | def send(self, request): 63 | self.addcredentials(request) 64 | return HttpTransport.send(self, request) 65 | 66 | def addcredentials(self, request): 67 | credentials = self.credentials() 68 | if not (None in credentials): 69 | u = credentials[0] 70 | p = credentials[1] 71 | self.pm.add_password(None, request.url, u, p) 72 | 73 | def credentials(self): 74 | return (self.options.username, self.options.password) 75 | 76 | def u2handlers(self): 77 | handlers = HttpTransport.u2handlers(self) 78 | handlers.append(u2.HTTPBasicAuthHandler(self.pm)) 79 | return handlers 80 | 81 | 82 | class WindowsHttpAuthenticated(HttpAuthenticated): 83 | """ 84 | Provides Windows (NTLM) http authentication. 85 | @ivar pm: The password manager. 86 | @ivar handler: The authentication handler. 87 | @author: Christopher Bess 88 | """ 89 | 90 | def u2handlers(self): 91 | # try to import ntlm support 92 | try: 93 | from ntlm import HTTPNtlmAuthHandler 94 | except ImportError: 95 | raise Exception("Cannot import python-ntlm module") 96 | handlers = HttpTransport.u2handlers(self) 97 | handlers.append(HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(self.pm)) 98 | return handlers 99 | -------------------------------------------------------------------------------- /bin/suds/transport/options.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains classes for transport options. 19 | """ 20 | 21 | 22 | from suds.transport import * 23 | from suds.properties import * 24 | 25 | 26 | class Options(Skin): 27 | """ 28 | Options: 29 | - B{proxy} - An http proxy to be specified on requests. 30 | The proxy is defined as {protocol:proxy,} 31 | - type: I{dict} 32 | - default: {} 33 | - B{timeout} - Set the url open timeout (seconds). 34 | - type: I{float} 35 | - default: 90 36 | - B{headers} - Extra HTTP headers. 37 | - type: I{dict} 38 | - I{str} B{http} - The I{http} protocol proxy URL. 39 | - I{str} B{https} - The I{https} protocol proxy URL. 40 | - default: {} 41 | - B{username} - The username used for http authentication. 42 | - type: I{str} 43 | - default: None 44 | - B{password} - The password used for http authentication. 45 | - type: I{str} 46 | - default: None 47 | """ 48 | def __init__(self, **kwargs): 49 | domain = __name__ 50 | definitions = [ 51 | Definition('proxy', dict, {}), 52 | Definition('timeout', (int,float), 90), 53 | Definition('headers', dict, {}), 54 | Definition('username', basestring, None), 55 | Definition('password', basestring, None), 56 | ] 57 | Skin.__init__(self, domain, definitions, kwargs) -------------------------------------------------------------------------------- /bin/suds/umx/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides modules containing classes to support 19 | unmarshalling (XML). 20 | """ 21 | 22 | from suds.sudsobject import Object 23 | 24 | 25 | 26 | class Content(Object): 27 | """ 28 | @ivar node: The content source node. 29 | @type node: L{sax.element.Element} 30 | @ivar data: The (optional) content data. 31 | @type data: L{Object} 32 | @ivar text: The (optional) content (xml) text. 33 | @type text: basestring 34 | """ 35 | 36 | extensions = [] 37 | 38 | def __init__(self, node, **kwargs): 39 | Object.__init__(self) 40 | self.node = node 41 | self.data = None 42 | self.text = None 43 | for k,v in kwargs.items(): 44 | setattr(self, k, v) 45 | 46 | def __getattr__(self, name): 47 | if name not in self.__dict__: 48 | if name in self.extensions: 49 | v = None 50 | setattr(self, name, v) 51 | else: 52 | raise AttributeError, \ 53 | 'Content has no attribute %s' % name 54 | else: 55 | v = self.__dict__[name] 56 | return v -------------------------------------------------------------------------------- /bin/suds/umx/attrlist.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides filtered attribute list classes. 19 | """ 20 | 21 | from suds import * 22 | from suds.umx import * 23 | from suds.sax import Namespace 24 | 25 | 26 | class AttrList: 27 | """ 28 | A filtered attribute list. 29 | Items are included during iteration if they are in either the (xs) or 30 | (xml) namespaces. 31 | @ivar raw: The I{raw} attribute list. 32 | @type raw: list 33 | """ 34 | def __init__(self, attributes): 35 | """ 36 | @param attributes: A list of attributes 37 | @type attributes: list 38 | """ 39 | self.raw = attributes 40 | 41 | def real(self): 42 | """ 43 | Get list of I{real} attributes which exclude xs and xml attributes. 44 | @return: A list of I{real} attributes. 45 | @rtype: I{generator} 46 | """ 47 | for a in self.raw: 48 | if self.skip(a): continue 49 | yield a 50 | 51 | def rlen(self): 52 | """ 53 | Get the number of I{real} attributes which exclude xs and xml attributes. 54 | @return: A count of I{real} attributes. 55 | @rtype: L{int} 56 | """ 57 | n = 0 58 | for a in self.real(): 59 | n += 1 60 | return n 61 | 62 | def lang(self): 63 | """ 64 | Get list of I{filtered} attributes which exclude xs. 65 | @return: A list of I{filtered} attributes. 66 | @rtype: I{generator} 67 | """ 68 | for a in self.raw: 69 | if a.qname() == 'xml:lang': 70 | return a.value 71 | return None 72 | 73 | def skip(self, attr): 74 | """ 75 | Get whether to skip (filter-out) the specified attribute. 76 | @param attr: An attribute. 77 | @type attr: I{Attribute} 78 | @return: True if should be skipped. 79 | @rtype: bool 80 | """ 81 | ns = attr.namespace() 82 | skip = ( 83 | Namespace.xmlns[1], 84 | 'http://schemas.xmlsoap.org/soap/encoding/', 85 | 'http://schemas.xmlsoap.org/soap/envelope/', 86 | 'http://www.w3.org/2003/05/soap-envelope', 87 | ) 88 | return ( Namespace.xs(ns) or ns[1] in skip ) 89 | -------------------------------------------------------------------------------- /bin/suds/umx/basic.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides basic unmarshaller classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.umx import * 24 | from suds.umx.core import Core 25 | 26 | 27 | class Basic(Core): 28 | """ 29 | A object builder (unmarshaller). 30 | """ 31 | 32 | def process(self, node): 33 | """ 34 | Process an object graph representation of the xml I{node}. 35 | @param node: An XML tree. 36 | @type node: L{sax.element.Element} 37 | @return: A suds object. 38 | @rtype: L{Object} 39 | """ 40 | content = Content(node) 41 | return Core.process(self, content) -------------------------------------------------------------------------------- /bin/suds/umx/core.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides base classes for XML->object I{unmarshalling}. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.umx import * 24 | from suds.umx.attrlist import AttrList 25 | from suds.sax.text import Text 26 | from suds.sudsobject import Factory, merge 27 | 28 | 29 | log = getLogger(__name__) 30 | 31 | reserved = { 'class':'cls', 'def':'dfn', } 32 | 33 | class Core: 34 | """ 35 | The abstract XML I{node} unmarshaller. This class provides the 36 | I{core} unmarshalling functionality. 37 | """ 38 | 39 | def process(self, content): 40 | """ 41 | Process an object graph representation of the xml I{node}. 42 | @param content: The current content being unmarshalled. 43 | @type content: L{Content} 44 | @return: A suds object. 45 | @rtype: L{Object} 46 | """ 47 | self.reset() 48 | return self.append(content) 49 | 50 | def append(self, content): 51 | """ 52 | Process the specified node and convert the XML document into 53 | a I{suds} L{object}. 54 | @param content: The current content being unmarshalled. 55 | @type content: L{Content} 56 | @return: A I{append-result} tuple as: (L{Object}, I{value}) 57 | @rtype: I{append-result} 58 | @note: This is not the proper entry point. 59 | @see: L{process()} 60 | """ 61 | self.start(content) 62 | self.append_attributes(content) 63 | self.append_children(content) 64 | self.append_text(content) 65 | self.end(content) 66 | return self.postprocess(content) 67 | 68 | def postprocess(self, content): 69 | """ 70 | Perform final processing of the resulting data structure as follows: 71 | - Mixed values (children and text) will have a result of the I{content.node}. 72 | - Simi-simple values (attributes, no-children and text) will have a result of a 73 | property object. 74 | - Simple values (no-attributes, no-children with text nodes) will have a string 75 | result equal to the value of the content.node.getText(). 76 | @param content: The current content being unmarshalled. 77 | @type content: L{Content} 78 | @return: The post-processed result. 79 | @rtype: I{any} 80 | """ 81 | node = content.node 82 | if len(node.children) and node.hasText(): 83 | return node 84 | attributes = AttrList(node.attributes) 85 | if attributes.rlen() and \ 86 | not len(node.children) and \ 87 | node.hasText(): 88 | p = Factory.property(node.name, node.getText()) 89 | return merge(content.data, p) 90 | if len(content.data): 91 | return content.data 92 | lang = attributes.lang() 93 | if content.node.isnil(): 94 | return None 95 | if not len(node.children) and content.text is None: 96 | if self.nillable(content): 97 | return None 98 | else: 99 | return Text('', lang=lang) 100 | if isinstance(content.text, basestring): 101 | return Text(content.text, lang=lang) 102 | else: 103 | return content.text 104 | 105 | def append_attributes(self, content): 106 | """ 107 | Append attribute nodes into L{Content.data}. 108 | Attributes in the I{schema} or I{xml} namespaces are skipped. 109 | @param content: The current content being unmarshalled. 110 | @type content: L{Content} 111 | """ 112 | attributes = AttrList(content.node.attributes) 113 | for attr in attributes.real(): 114 | name = attr.name 115 | value = attr.value 116 | self.append_attribute(name, value, content) 117 | 118 | def append_attribute(self, name, value, content): 119 | """ 120 | Append an attribute name/value into L{Content.data}. 121 | @param name: The attribute name 122 | @type name: basestring 123 | @param value: The attribute's value 124 | @type value: basestring 125 | @param content: The current content being unmarshalled. 126 | @type content: L{Content} 127 | """ 128 | key = name 129 | key = '_%s' % reserved.get(key, key) 130 | setattr(content.data, key, value) 131 | 132 | def append_children(self, content): 133 | """ 134 | Append child nodes into L{Content.data} 135 | @param content: The current content being unmarshalled. 136 | @type content: L{Content} 137 | """ 138 | for child in content.node: 139 | cont = Content(child) 140 | cval = self.append(cont) 141 | key = reserved.get(child.name, child.name) 142 | if key in content.data: 143 | v = getattr(content.data, key) 144 | if isinstance(v, list): 145 | v.append(cval) 146 | else: 147 | setattr(content.data, key, [v, cval]) 148 | continue 149 | if self.unbounded(cont): 150 | if cval is None: 151 | setattr(content.data, key, []) 152 | else: 153 | setattr(content.data, key, [cval,]) 154 | else: 155 | setattr(content.data, key, cval) 156 | 157 | def append_text(self, content): 158 | """ 159 | Append text nodes into L{Content.data} 160 | @param content: The current content being unmarshalled. 161 | @type content: L{Content} 162 | """ 163 | if content.node.hasText(): 164 | content.text = content.node.getText() 165 | 166 | def reset(self): 167 | pass 168 | 169 | def start(self, content): 170 | """ 171 | Processing on I{node} has started. Build and return 172 | the proper object. 173 | @param content: The current content being unmarshalled. 174 | @type content: L{Content} 175 | @return: A subclass of Object. 176 | @rtype: L{Object} 177 | """ 178 | content.data = Factory.object(content.node.name) 179 | 180 | def end(self, content): 181 | """ 182 | Processing on I{node} has ended. 183 | @param content: The current content being unmarshalled. 184 | @type content: L{Content} 185 | """ 186 | pass 187 | 188 | def bounded(self, content): 189 | """ 190 | Get whether the content is bounded (not a list). 191 | @param content: The current content being unmarshalled. 192 | @type content: L{Content} 193 | @return: True if bounded, else False 194 | @rtype: boolean 195 | '""" 196 | return ( not self.unbounded(content) ) 197 | 198 | def unbounded(self, content): 199 | """ 200 | Get whether the object is unbounded (a list). 201 | @param content: The current content being unmarshalled. 202 | @type content: L{Content} 203 | @return: True if unbounded, else False 204 | @rtype: boolean 205 | '""" 206 | return False 207 | 208 | def nillable(self, content): 209 | """ 210 | Get whether the object is nillable. 211 | @param content: The current content being unmarshalled. 212 | @type content: L{Content} 213 | @return: True if nillable, else False 214 | @rtype: boolean 215 | '""" 216 | return False -------------------------------------------------------------------------------- /bin/suds/umx/encoded.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides soap encoded unmarshaller classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.umx import * 24 | from suds.umx.typed import Typed 25 | from suds.sax import splitPrefix, Namespace 26 | 27 | log = getLogger(__name__) 28 | 29 | # 30 | # Add encoded extensions 31 | # aty = The soap (section 5) encoded array type. 32 | # 33 | Content.extensions.append('aty') 34 | 35 | 36 | class Encoded(Typed): 37 | """ 38 | A SOAP section (5) encoding unmarshaller. 39 | This marshaller supports rpc/encoded soap styles. 40 | """ 41 | 42 | def start(self, content): 43 | # 44 | # Grab the array type and continue 45 | # 46 | self.setaty(content) 47 | Typed.start(self, content) 48 | 49 | def end(self, content): 50 | # 51 | # Squash soap encoded arrays into python lists. This is 52 | # also where we insure that empty arrays are represented 53 | # as empty python lists. 54 | # 55 | aty = content.aty 56 | if aty is not None: 57 | self.promote(content) 58 | return Typed.end(self, content) 59 | 60 | def postprocess(self, content): 61 | # 62 | # Ensure proper rendering of empty arrays. 63 | # 64 | if content.aty is None: 65 | return Typed.postprocess(self, content) 66 | else: 67 | return content.data 68 | 69 | def setaty(self, content): 70 | """ 71 | Grab the (aty) soap-enc:arrayType and attach it to the 72 | content for proper array processing later in end(). 73 | @param content: The current content being unmarshalled. 74 | @type content: L{Content} 75 | @return: self 76 | @rtype: L{Encoded} 77 | """ 78 | name = 'arrayType' 79 | ns = (None, 'http://schemas.xmlsoap.org/soap/encoding/') 80 | aty = content.node.get(name, ns) 81 | if aty is not None: 82 | content.aty = aty 83 | parts = aty.split('[') 84 | ref = parts[0] 85 | if len(parts) == 2: 86 | self.applyaty(content, ref) 87 | else: 88 | pass # (2) dimensional array 89 | return self 90 | 91 | def applyaty(self, content, xty): 92 | """ 93 | Apply the type referenced in the I{arrayType} to the content 94 | (child nodes) of the array. Each element (node) in the array 95 | that does not have an explicit xsi:type attribute is given one 96 | based on the I{arrayType}. 97 | @param content: An array content. 98 | @type content: L{Content} 99 | @param xty: The XSI type reference. 100 | @type xty: str 101 | @return: self 102 | @rtype: L{Encoded} 103 | """ 104 | name = 'type' 105 | ns = Namespace.xsins 106 | parent = content.node 107 | for child in parent.getChildren(): 108 | ref = child.get(name, ns) 109 | if ref is None: 110 | parent.addPrefix(ns[0], ns[1]) 111 | attr = ':'.join((ns[0], name)) 112 | child.set(attr, xty) 113 | return self 114 | 115 | def promote(self, content): 116 | """ 117 | Promote (replace) the content.data with the first attribute 118 | of the current content.data that is a I{list}. Note: the 119 | content.data may be empty or contain only _x attributes. 120 | In either case, the content.data is assigned an empty list. 121 | @param content: An array content. 122 | @type content: L{Content} 123 | """ 124 | for n,v in content.data: 125 | if isinstance(v, list): 126 | content.data = v 127 | return 128 | content.data = [] -------------------------------------------------------------------------------- /bin/suds/umx/typed.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides typed unmarshaller classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.umx import * 24 | from suds.umx.core import Core 25 | from suds.resolver import NodeResolver, Frame 26 | from suds.sudsobject import Factory 27 | 28 | log = getLogger(__name__) 29 | 30 | 31 | # 32 | # Add typed extensions 33 | # type = The expected xsd type 34 | # real = The 'true' XSD type 35 | # 36 | Content.extensions.append('type') 37 | Content.extensions.append('real') 38 | 39 | 40 | class Typed(Core): 41 | """ 42 | A I{typed} XML unmarshaller 43 | @ivar resolver: A schema type resolver. 44 | @type resolver: L{NodeResolver} 45 | """ 46 | 47 | def __init__(self, schema): 48 | """ 49 | @param schema: A schema object. 50 | @type schema: L{xsd.schema.Schema} 51 | """ 52 | self.resolver = NodeResolver(schema) 53 | 54 | def process(self, node, type): 55 | """ 56 | Process an object graph representation of the xml L{node}. 57 | @param node: An XML tree. 58 | @type node: L{sax.element.Element} 59 | @param type: The I{optional} schema type. 60 | @type type: L{xsd.sxbase.SchemaObject} 61 | @return: A suds object. 62 | @rtype: L{Object} 63 | """ 64 | content = Content(node) 65 | content.type = type 66 | return Core.process(self, content) 67 | 68 | def reset(self): 69 | log.debug('reset') 70 | self.resolver.reset() 71 | 72 | def start(self, content): 73 | # 74 | # Resolve to the schema type; build an object and setup metadata. 75 | # 76 | if content.type is None: 77 | found = self.resolver.find(content.node) 78 | if found is None: 79 | log.error(self.resolver.schema) 80 | raise TypeNotFound(content.node.qname()) 81 | content.type = found 82 | else: 83 | known = self.resolver.known(content.node) 84 | frame = Frame(content.type, resolved=known) 85 | self.resolver.push(frame) 86 | real = self.resolver.top().resolved 87 | content.real = real 88 | cls_name = real.name 89 | if cls_name is None: 90 | cls_name = content.node.name 91 | content.data = Factory.object(cls_name) 92 | md = content.data.__metadata__ 93 | md.sxtype = real 94 | 95 | def end(self, content): 96 | self.resolver.pop() 97 | 98 | def unbounded(self, content): 99 | return content.type.unbounded() 100 | 101 | def nillable(self, content): 102 | resolved = content.type.resolve() 103 | return ( content.type.nillable or \ 104 | (resolved.builtin() and resolved.nillable ) ) 105 | 106 | def append_attribute(self, name, value, content): 107 | """ 108 | Append an attribute name/value into L{Content.data}. 109 | @param name: The attribute name 110 | @type name: basestring 111 | @param value: The attribute's value 112 | @type value: basestring 113 | @param content: The current content being unmarshalled. 114 | @type content: L{Content} 115 | """ 116 | type = self.resolver.findattr(name) 117 | if type is None: 118 | log.warn('attribute (%s) type, not-found', name) 119 | else: 120 | value = self.translated(value, type) 121 | Core.append_attribute(self, name, value, content) 122 | 123 | def append_text(self, content): 124 | """ 125 | Append text nodes into L{Content.data} 126 | Here is where the I{true} type is used to translate the value 127 | into the proper python type. 128 | @param content: The current content being unmarshalled. 129 | @type content: L{Content} 130 | """ 131 | Core.append_text(self, content) 132 | known = self.resolver.top().resolved 133 | content.text = self.translated(content.text, known) 134 | 135 | def translated(self, value, type): 136 | """ translate using the schema type """ 137 | if value is not None: 138 | resolved = type.resolve() 139 | return resolved.translate(value) 140 | else: 141 | return value -------------------------------------------------------------------------------- /bin/suds/wsse.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{wsse} module provides WS-Security. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sudsobject import Object 24 | from suds.sax.element import Element 25 | from suds.sax.date import UTC 26 | from datetime import datetime, timedelta 27 | 28 | try: 29 | from hashlib import md5 30 | except ImportError: 31 | # Python 2.4 compatibility 32 | from md5 import md5 33 | 34 | 35 | dsns = \ 36 | ('ds', 37 | 'http://www.w3.org/2000/09/xmldsig#') 38 | wssens = \ 39 | ('wsse', 40 | 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd') 41 | wsuns = \ 42 | ('wsu', 43 | 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd') 44 | wsencns = \ 45 | ('wsenc', 46 | 'http://www.w3.org/2001/04/xmlenc#') 47 | 48 | 49 | class Security(Object): 50 | """ 51 | WS-Security object. 52 | @ivar tokens: A list of security tokens 53 | @type tokens: [L{Token},...] 54 | @ivar signatures: A list of signatures. 55 | @type signatures: TBD 56 | @ivar references: A list of references. 57 | @type references: TBD 58 | @ivar keys: A list of encryption keys. 59 | @type keys: TBD 60 | """ 61 | 62 | def __init__(self): 63 | """ """ 64 | Object.__init__(self) 65 | self.mustUnderstand = True 66 | self.tokens = [] 67 | self.signatures = [] 68 | self.references = [] 69 | self.keys = [] 70 | 71 | def xml(self): 72 | """ 73 | Get xml representation of the object. 74 | @return: The root node. 75 | @rtype: L{Element} 76 | """ 77 | root = Element('Security', ns=wssens) 78 | root.set('mustUnderstand', str(self.mustUnderstand).lower()) 79 | for t in self.tokens: 80 | root.append(t.xml()) 81 | return root 82 | 83 | 84 | class Token(Object): 85 | """ I{Abstract} security token. """ 86 | 87 | @classmethod 88 | def now(cls): 89 | return datetime.now() 90 | 91 | @classmethod 92 | def utc(cls): 93 | return datetime.utcnow() 94 | 95 | @classmethod 96 | def sysdate(cls): 97 | utc = UTC() 98 | return str(utc) 99 | 100 | def __init__(self): 101 | Object.__init__(self) 102 | 103 | 104 | class UsernameToken(Token): 105 | """ 106 | Represents a basic I{UsernameToken} WS-Secuirty token. 107 | @ivar username: A username. 108 | @type username: str 109 | @ivar password: A password. 110 | @type password: str 111 | @ivar nonce: A set of bytes to prevent reply attacks. 112 | @type nonce: str 113 | @ivar created: The token created. 114 | @type created: L{datetime} 115 | """ 116 | 117 | def __init__(self, username=None, password=None): 118 | """ 119 | @param username: A username. 120 | @type username: str 121 | @param password: A password. 122 | @type password: str 123 | """ 124 | Token.__init__(self) 125 | self.username = username 126 | self.password = password 127 | self.nonce = None 128 | self.created = None 129 | 130 | def setnonce(self, text=None): 131 | """ 132 | Set I{nonce} which is arbitraty set of bytes to prevent 133 | reply attacks. 134 | @param text: The nonce text value. 135 | Generated when I{None}. 136 | @type text: str 137 | """ 138 | if text is None: 139 | s = [] 140 | s.append(self.username) 141 | s.append(self.password) 142 | s.append(Token.sysdate()) 143 | m = md5() 144 | m.update(':'.join(s)) 145 | self.nonce = m.hexdigest() 146 | else: 147 | self.nonce = text 148 | 149 | def setcreated(self, dt=None): 150 | """ 151 | Set I{created}. 152 | @param dt: The created date & time. 153 | Set as datetime.utc() when I{None}. 154 | @type dt: L{datetime} 155 | """ 156 | if dt is None: 157 | self.created = Token.utc() 158 | else: 159 | self.created = dt 160 | 161 | 162 | def xml(self): 163 | """ 164 | Get xml representation of the object. 165 | @return: The root node. 166 | @rtype: L{Element} 167 | """ 168 | root = Element('UsernameToken', ns=wssens) 169 | u = Element('Username', ns=wssens) 170 | u.setText(self.username) 171 | root.append(u) 172 | p = Element('Password', ns=wssens) 173 | p.setText(self.password) 174 | root.append(p) 175 | if self.nonce is not None: 176 | n = Element('Nonce', ns=wssens) 177 | n.setText(self.nonce) 178 | root.append(n) 179 | if self.created is not None: 180 | n = Element('Created', ns=wsuns) 181 | n.setText(str(UTC(self.created))) 182 | root.append(n) 183 | return root 184 | 185 | 186 | class Timestamp(Token): 187 | """ 188 | Represents the I{Timestamp} WS-Secuirty token. 189 | @ivar created: The token created. 190 | @type created: L{datetime} 191 | @ivar expires: The token expires. 192 | @type expires: L{datetime} 193 | """ 194 | 195 | def __init__(self, validity=90): 196 | """ 197 | @param validity: The time in seconds. 198 | @type validity: int 199 | """ 200 | Token.__init__(self) 201 | self.created = Token.utc() 202 | self.expires = self.created + timedelta(seconds=validity) 203 | 204 | def xml(self): 205 | root = Element("Timestamp", ns=wsuns) 206 | created = Element('Created', ns=wsuns) 207 | created.setText(str(UTC(self.created))) 208 | expires = Element('Expires', ns=wsuns) 209 | expires.setText(str(UTC(self.expires))) 210 | root.append(created) 211 | root.append(expires) 212 | return root -------------------------------------------------------------------------------- /bin/suds/xsd/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{schema} module provides a intelligent representation of 19 | an XSD schema. The I{raw} model is the XML tree and the I{model} 20 | is the denormalized, objectified and intelligent view of the schema. 21 | Most of the I{value-add} provided by the model is centered around 22 | tranparent referenced type resolution and targeted denormalization. 23 | """ 24 | 25 | from logging import getLogger 26 | from suds import * 27 | from suds.sax import Namespace, splitPrefix 28 | 29 | log = getLogger(__name__) 30 | 31 | 32 | def qualify(ref, resolvers, defns=Namespace.default): 33 | """ 34 | Get a reference that is I{qualified} by namespace. 35 | @param ref: A referenced schema type name. 36 | @type ref: str 37 | @param resolvers: A list of objects to be used to resolve types. 38 | @type resolvers: [L{sax.element.Element},] 39 | @param defns: An optional target namespace used to qualify references 40 | when no prefix is specified. 41 | @type defns: A default namespace I{tuple: (prefix,uri)} used when ref not prefixed. 42 | @return: A qualified reference. 43 | @rtype: (name, namespace-uri) 44 | """ 45 | ns = None 46 | p, n = splitPrefix(ref) 47 | if p is not None: 48 | if not isinstance(resolvers, (list, tuple)): 49 | resolvers = (resolvers,) 50 | for r in resolvers: 51 | resolved = r.resolvePrefix(p) 52 | if resolved[1] is not None: 53 | ns = resolved 54 | break 55 | if ns is None: 56 | raise Exception('prefix (%s) not resolved' % p) 57 | else: 58 | ns = defns 59 | return (n, ns[1]) 60 | 61 | def isqref(object): 62 | """ 63 | Get whether the object is a I{qualified reference}. 64 | @param object: An object to be tested. 65 | @type object: I{any} 66 | @rtype: boolean 67 | @see: L{qualify} 68 | """ 69 | return (\ 70 | isinstance(object, tuple) and \ 71 | len(object) == 2 and \ 72 | isinstance(object[0], basestring) and \ 73 | isinstance(object[1], basestring)) 74 | 75 | 76 | class Filter: 77 | def __init__(self, inclusive=False, *items): 78 | self.inclusive = inclusive 79 | self.items = items 80 | def __contains__(self, x): 81 | if self.inclusive: 82 | result = ( x in self.items ) 83 | else: 84 | result = ( x not in self.items ) 85 | return result 86 | 87 | -------------------------------------------------------------------------------- /bin/suds/xsd/deplist.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{depsolve} module defines a class for performing dependancy solving. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | 24 | log = getLogger(__name__) 25 | 26 | 27 | class DepList: 28 | """ 29 | Dependancy solving list. 30 | Items are tuples: (object, (deps,)) 31 | @ivar raw: The raw (unsorted) items. 32 | @type raw: list 33 | @ivar index: The index of (unsorted) items. 34 | @type index: list 35 | @ivar stack: The sorting stack. 36 | @type stack: list 37 | @ivar pushed: The I{pushed} set tracks items that have been 38 | processed. 39 | @type pushed: set 40 | @ivar sorted: The sorted list of items. 41 | @type sorted: list 42 | """ 43 | 44 | def __init__(self): 45 | """ """ 46 | self.unsorted = [] 47 | self.index = {} 48 | self.stack = [] 49 | self.pushed = set() 50 | self.sorted = None 51 | 52 | def add(self, *items): 53 | """ 54 | Add items to be sorted. 55 | @param items: One or more items to be added. 56 | @type items: I{item} 57 | @return: self 58 | @rtype: L{DepList} 59 | """ 60 | for item in items: 61 | self.unsorted.append(item) 62 | key = item[0] 63 | self.index[key] = item 64 | return self 65 | 66 | def sort(self): 67 | """ 68 | Sort the list based on dependancies. 69 | @return: The sorted items. 70 | @rtype: list 71 | """ 72 | self.sorted = list() 73 | self.pushed = set() 74 | for item in self.unsorted: 75 | popped = [] 76 | self.push(item) 77 | while len(self.stack): 78 | try: 79 | top = self.top() 80 | ref = top[1].next() 81 | refd = self.index.get(ref) 82 | if refd is None: 83 | log.debug('"%s" not found, skipped', Repr(ref)) 84 | continue 85 | self.push(refd) 86 | except StopIteration: 87 | popped.append(self.pop()) 88 | continue 89 | for p in popped: 90 | self.sorted.append(p) 91 | self.unsorted = self.sorted 92 | return self.sorted 93 | 94 | def top(self): 95 | """ 96 | Get the item at the top of the stack. 97 | @return: The top item. 98 | @rtype: (item, iter) 99 | """ 100 | return self.stack[-1] 101 | 102 | def push(self, item): 103 | """ 104 | Push and item onto the sorting stack. 105 | @param item: An item to push. 106 | @type item: I{item} 107 | @return: The number of items pushed. 108 | @rtype: int 109 | """ 110 | if item in self.pushed: 111 | return 112 | frame = (item, iter(item[1])) 113 | self.stack.append(frame) 114 | self.pushed.add(item) 115 | 116 | def pop(self): 117 | """ 118 | Pop the top item off the stack and append 119 | it to the sorted list. 120 | @return: The popped item. 121 | @rtype: I{item} 122 | """ 123 | try: 124 | frame = self.stack.pop() 125 | return frame[0] 126 | except: 127 | pass 128 | 129 | 130 | if __name__ == '__main__': 131 | a = ('a', ('x',)) 132 | b = ('b', ('a',)) 133 | c = ('c', ('a','b')) 134 | d = ('d', ('c',)) 135 | e = ('e', ('d','a')) 136 | f = ('f', ('e','c','d','a')) 137 | x = ('x', ()) 138 | L = DepList() 139 | L.add(c, e, d, b, f, a, x) 140 | print [x[0] for x in L.sort()] -------------------------------------------------------------------------------- /bin/suds/xsd/doctor.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{doctor} module provides classes for fixing broken (sick) 19 | schema(s). 20 | """ 21 | 22 | from logging import getLogger 23 | from suds.sax import splitPrefix, Namespace 24 | from suds.sax.element import Element 25 | from suds.plugin import DocumentPlugin, DocumentContext 26 | 27 | log = getLogger(__name__) 28 | 29 | 30 | class Doctor: 31 | """ 32 | Schema Doctor. 33 | """ 34 | def examine(self, root): 35 | """ 36 | Examine and repair the schema (if necessary). 37 | @param root: A schema root element. 38 | @type root: L{Element} 39 | """ 40 | pass 41 | 42 | 43 | class Practice(Doctor): 44 | """ 45 | A collection of doctors. 46 | @ivar doctors: A list of doctors. 47 | @type doctors: list 48 | """ 49 | 50 | def __init__(self): 51 | self.doctors = [] 52 | 53 | def add(self, doctor): 54 | """ 55 | Add a doctor to the practice 56 | @param doctor: A doctor to add. 57 | @type doctor: L{Doctor} 58 | """ 59 | self.doctors.append(doctor) 60 | 61 | def examine(self, root): 62 | for d in self.doctors: 63 | d.examine(root) 64 | return root 65 | 66 | 67 | class TnsFilter: 68 | """ 69 | Target Namespace filter. 70 | @ivar tns: A list of target namespaces. 71 | @type tns: [str,...] 72 | """ 73 | 74 | def __init__(self, *tns): 75 | """ 76 | @param tns: A list of target namespaces. 77 | @type tns: [str,...] 78 | """ 79 | self.tns = [] 80 | self.add(*tns) 81 | 82 | def add(self, *tns): 83 | """ 84 | Add I{targetNamesapces} to be added. 85 | @param tns: A list of target namespaces. 86 | @type tns: [str,...] 87 | """ 88 | self.tns += tns 89 | 90 | def match(self, root, ns): 91 | """ 92 | Match by I{targetNamespace} excluding those that 93 | are equal to the specified namespace to prevent 94 | adding an import to itself. 95 | @param root: A schema root. 96 | @type root: L{Element} 97 | """ 98 | tns = root.get('targetNamespace') 99 | if len(self.tns): 100 | matched = ( tns in self.tns ) 101 | else: 102 | matched = 1 103 | itself = ( ns == tns ) 104 | return ( matched and not itself ) 105 | 106 | 107 | class Import: 108 | """ 109 | An to be applied. 110 | @cvar xsdns: The XSD namespace. 111 | @type xsdns: (p,u) 112 | @ivar ns: An import namespace. 113 | @type ns: str 114 | @ivar location: An optional I{schemaLocation}. 115 | @type location: str 116 | @ivar filter: A filter used to restrict application to 117 | a particular schema. 118 | @type filter: L{TnsFilter} 119 | """ 120 | 121 | xsdns = Namespace.xsdns 122 | 123 | def __init__(self, ns, location=None): 124 | """ 125 | @param ns: An import namespace. 126 | @type ns: str 127 | @param location: An optional I{schemaLocation}. 128 | @type location: str 129 | """ 130 | self.ns = ns 131 | self.location = location 132 | self.filter = TnsFilter() 133 | 134 | def setfilter(self, filter): 135 | """ 136 | Set the filter. 137 | @param filter: A filter to set. 138 | @type filter: L{TnsFilter} 139 | """ 140 | self.filter = filter 141 | 142 | def apply(self, root): 143 | """ 144 | Apply the import (rule) to the specified schema. 145 | If the schema does not already contain an import for the 146 | I{namespace} specified here, it is added. 147 | @param root: A schema root. 148 | @type root: L{Element} 149 | """ 150 | if not self.filter.match(root, self.ns): 151 | return 152 | if self.exists(root): 153 | return 154 | node = Element('import', ns=self.xsdns) 155 | node.set('namespace', self.ns) 156 | if self.location is not None: 157 | node.set('schemaLocation', self.location) 158 | log.debug('inserting: %s', node) 159 | root.insert(node) 160 | 161 | def add(self, root): 162 | """ 163 | Add an to the specified schema root. 164 | @param root: A schema root. 165 | @type root: L{Element} 166 | """ 167 | node = Element('import', ns=self.xsdns) 168 | node.set('namespace', self.ns) 169 | if self.location is not None: 170 | node.set('schemaLocation', self.location) 171 | log.debug('%s inserted', node) 172 | root.insert(node) 173 | 174 | def exists(self, root): 175 | """ 176 | Check to see if the already exists 177 | in the specified schema root by matching I{namesapce}. 178 | @param root: A schema root. 179 | @type root: L{Element} 180 | """ 181 | for node in root.children: 182 | if node.name != 'import': 183 | continue 184 | ns = node.get('namespace') 185 | if self.ns == ns: 186 | return 1 187 | return 0 188 | 189 | 190 | class ImportDoctor(Doctor, DocumentPlugin): 191 | """ 192 | Doctor used to fix missing imports. 193 | @ivar imports: A list of imports to apply. 194 | @type imports: [L{Import},...] 195 | """ 196 | 197 | def __init__(self, *imports): 198 | """ 199 | """ 200 | self.imports = [] 201 | self.add(*imports) 202 | 203 | def add(self, *imports): 204 | """ 205 | Add a namesapce to be checked. 206 | @param imports: A list of L{Import} objects. 207 | @type imports: [L{Import},..] 208 | """ 209 | self.imports += imports 210 | 211 | def examine(self, node): 212 | for imp in self.imports: 213 | imp.apply(node) 214 | 215 | def parsed(self, context): 216 | node = context.document 217 | # xsd root 218 | if node.name == 'schema' and Namespace.xsd(node.namespace()): 219 | self.examine(node) 220 | return 221 | # look deeper 222 | context = DocumentContext() 223 | for child in node: 224 | context.document = child 225 | self.parsed(context) 226 | -------------------------------------------------------------------------------- /bin/suds/xsd/query.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{query} module defines a class for performing schema queries. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sudsobject import * 24 | from suds.xsd import qualify, isqref 25 | from suds.xsd.sxbuiltin import Factory 26 | 27 | log = getLogger(__name__) 28 | 29 | 30 | class Query(Object): 31 | """ 32 | Schema query base class. 33 | """ 34 | 35 | def __init__(self, ref=None): 36 | """ 37 | @param ref: The schema reference being queried. 38 | @type ref: qref 39 | """ 40 | Object.__init__(self) 41 | self.id = objid(self) 42 | self.ref = ref 43 | self.history = [] 44 | self.resolved = False 45 | if not isqref(self.ref): 46 | raise Exception('%s, must be qref' % tostr(self.ref)) 47 | 48 | def execute(self, schema): 49 | """ 50 | Execute this query using the specified schema. 51 | @param schema: The schema associated with the query. The schema 52 | is used by the query to search for items. 53 | @type schema: L{schema.Schema} 54 | @return: The item matching the search criteria. 55 | @rtype: L{sxbase.SchemaObject} 56 | """ 57 | raise Exception, 'not-implemented by subclass' 58 | 59 | def filter(self, result): 60 | """ 61 | Filter the specified result based on query criteria. 62 | @param result: A potential result. 63 | @type result: L{sxbase.SchemaObject} 64 | @return: True if result should be excluded. 65 | @rtype: boolean 66 | """ 67 | if result is None: 68 | return True 69 | reject = ( result in self.history ) 70 | if reject: 71 | log.debug('result %s, rejected by\n%s', Repr(result), self) 72 | return reject 73 | 74 | def result(self, result): 75 | """ 76 | Query result post processing. 77 | @param result: A query result. 78 | @type result: L{sxbase.SchemaObject} 79 | """ 80 | if result is None: 81 | log.debug('%s, not-found', self.ref) 82 | return 83 | if self.resolved: 84 | result = result.resolve() 85 | log.debug('%s, found as: %s', self.ref, Repr(result)) 86 | self.history.append(result) 87 | return result 88 | 89 | 90 | class BlindQuery(Query): 91 | """ 92 | Schema query class that I{blindly} searches for a reference in 93 | the specified schema. It may be used to find Elements and Types but 94 | will match on an Element first. This query will also find builtins. 95 | """ 96 | 97 | def execute(self, schema): 98 | if schema.builtin(self.ref): 99 | name = self.ref[0] 100 | b = Factory.create(schema, name) 101 | log.debug('%s, found builtin (%s)', self.id, name) 102 | return b 103 | result = None 104 | for d in (schema.elements, schema.types): 105 | result = d.get(self.ref) 106 | if self.filter(result): 107 | result = None 108 | else: 109 | break 110 | if result is None: 111 | eq = ElementQuery(self.ref) 112 | eq.history = self.history 113 | result = eq.execute(schema) 114 | return self.result(result) 115 | 116 | 117 | class TypeQuery(Query): 118 | """ 119 | Schema query class that searches for Type references in 120 | the specified schema. Matches on root types only. 121 | """ 122 | 123 | def execute(self, schema): 124 | if schema.builtin(self.ref): 125 | name = self.ref[0] 126 | b = Factory.create(schema, name) 127 | log.debug('%s, found builtin (%s)', self.id, name) 128 | return b 129 | result = schema.types.get(self.ref) 130 | if self.filter(result): 131 | result = None 132 | return self.result(result) 133 | 134 | 135 | class GroupQuery(Query): 136 | """ 137 | Schema query class that searches for Group references in 138 | the specified schema. 139 | """ 140 | 141 | def execute(self, schema): 142 | result = schema.groups.get(self.ref) 143 | if self.filter(result): 144 | result = None 145 | return self.result(result) 146 | 147 | 148 | class AttrQuery(Query): 149 | """ 150 | Schema query class that searches for Attribute references in 151 | the specified schema. Matches on root Attribute by qname first, then searches 152 | deep into the document. 153 | """ 154 | 155 | def execute(self, schema): 156 | result = schema.attributes.get(self.ref) 157 | if self.filter(result): 158 | result = self.__deepsearch(schema) 159 | return self.result(result) 160 | 161 | def __deepsearch(self, schema): 162 | from suds.xsd.sxbasic import Attribute 163 | result = None 164 | for e in schema.all: 165 | result = e.find(self.ref, (Attribute,)) 166 | if self.filter(result): 167 | result = None 168 | else: 169 | break 170 | return result 171 | 172 | 173 | class AttrGroupQuery(Query): 174 | """ 175 | Schema query class that searches for attributeGroup references in 176 | the specified schema. 177 | """ 178 | 179 | def execute(self, schema): 180 | result = schema.agrps.get(self.ref) 181 | if self.filter(result): 182 | result = None 183 | return self.result(result) 184 | 185 | 186 | class ElementQuery(Query): 187 | """ 188 | Schema query class that searches for Element references in 189 | the specified schema. Matches on root Elements by qname first, then searches 190 | deep into the document. 191 | """ 192 | 193 | def execute(self, schema): 194 | result = schema.elements.get(self.ref) 195 | if self.filter(result): 196 | result = self.__deepsearch(schema) 197 | return self.result(result) 198 | 199 | def __deepsearch(self, schema): 200 | from suds.xsd.sxbasic import Element 201 | result = None 202 | for e in schema.all: 203 | result = e.find(self.ref, (Element,)) 204 | if self.filter(result): 205 | result = None 206 | else: 207 | break 208 | return result -------------------------------------------------------------------------------- /bin/suds/xsd/sxbuiltin.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{sxbuiltin} module provides classes that represent 19 | XSD I{builtin} schema objects. 20 | """ 21 | 22 | from logging import getLogger 23 | from suds import * 24 | from suds.xsd import * 25 | from suds.sax.date import * 26 | from suds.xsd.sxbase import XBuiltin 27 | import datetime as dt 28 | 29 | 30 | log = getLogger(__name__) 31 | 32 | 33 | class XString(XBuiltin): 34 | """ 35 | Represents an (xsd) node 36 | """ 37 | pass 38 | 39 | 40 | class XAny(XBuiltin): 41 | """ 42 | Represents an (xsd) node 43 | """ 44 | 45 | def __init__(self, schema, name): 46 | XBuiltin.__init__(self, schema, name) 47 | self.nillable = False 48 | 49 | def get_child(self, name): 50 | child = XAny(self.schema, name) 51 | return (child, []) 52 | 53 | def any(self): 54 | return True 55 | 56 | 57 | class XBoolean(XBuiltin): 58 | """ 59 | Represents an (xsd) boolean builtin type. 60 | """ 61 | 62 | translation = ( 63 | { '1':True,'true':True,'0':False,'false':False }, 64 | { True:'true',1:'true',False:'false',0:'false' }, 65 | ) 66 | 67 | def translate(self, value, topython=True): 68 | if topython: 69 | if isinstance(value, basestring): 70 | return XBoolean.translation[0].get(value) 71 | else: 72 | return None 73 | else: 74 | if isinstance(value, (bool,int)): 75 | return XBoolean.translation[1].get(value) 76 | else: 77 | return value 78 | 79 | 80 | class XInteger(XBuiltin): 81 | """ 82 | Represents an (xsd) xs:int builtin type. 83 | """ 84 | 85 | def translate(self, value, topython=True): 86 | if topython: 87 | if isinstance(value, basestring) and len(value): 88 | return int(value) 89 | else: 90 | return None 91 | else: 92 | if isinstance(value, int): 93 | return str(value) 94 | else: 95 | return value 96 | 97 | class XLong(XBuiltin): 98 | """ 99 | Represents an (xsd) xs:long builtin type. 100 | """ 101 | 102 | def translate(self, value, topython=True): 103 | if topython: 104 | if isinstance(value, basestring) and len(value): 105 | return long(value) 106 | else: 107 | return None 108 | else: 109 | if isinstance(value, (int,long)): 110 | return str(value) 111 | else: 112 | return value 113 | 114 | 115 | class XFloat(XBuiltin): 116 | """ 117 | Represents an (xsd) xs:float builtin type. 118 | """ 119 | 120 | def translate(self, value, topython=True): 121 | if topython: 122 | if isinstance(value, basestring) and len(value): 123 | return float(value) 124 | else: 125 | return None 126 | else: 127 | if isinstance(value, float): 128 | return str(value) 129 | else: 130 | return value 131 | 132 | 133 | class XDate(XBuiltin): 134 | """ 135 | Represents an (xsd) xs:date builtin type. 136 | """ 137 | 138 | def translate(self, value, topython=True): 139 | if topython: 140 | if isinstance(value, basestring) and len(value): 141 | return Date(value).date 142 | else: 143 | return None 144 | else: 145 | if isinstance(value, dt.date): 146 | return str(Date(value)) 147 | else: 148 | return value 149 | 150 | 151 | class XTime(XBuiltin): 152 | """ 153 | Represents an (xsd) xs:time builtin type. 154 | """ 155 | 156 | def translate(self, value, topython=True): 157 | if topython: 158 | if isinstance(value, basestring) and len(value): 159 | return Time(value).time 160 | else: 161 | return None 162 | else: 163 | if isinstance(value, dt.date): 164 | return str(Time(value)) 165 | else: 166 | return value 167 | 168 | 169 | class XDateTime(XBuiltin): 170 | """ 171 | Represents an (xsd) xs:datetime builtin type. 172 | """ 173 | 174 | def translate(self, value, topython=True): 175 | if topython: 176 | if isinstance(value, basestring) and len(value): 177 | return DateTime(value).datetime 178 | else: 179 | return None 180 | else: 181 | if isinstance(value, dt.date): 182 | return str(DateTime(value)) 183 | else: 184 | return value 185 | 186 | 187 | class Factory: 188 | 189 | tags =\ 190 | { 191 | # any 192 | 'anyType' : XAny, 193 | # strings 194 | 'string' : XString, 195 | 'normalizedString' : XString, 196 | 'ID' : XString, 197 | 'Name' : XString, 198 | 'QName' : XString, 199 | 'NCName' : XString, 200 | 'anySimpleType' : XString, 201 | 'anyURI' : XString, 202 | 'NOTATION' : XString, 203 | 'token' : XString, 204 | 'language' : XString, 205 | 'IDREFS' : XString, 206 | 'ENTITIES' : XString, 207 | 'IDREF' : XString, 208 | 'ENTITY' : XString, 209 | 'NMTOKEN' : XString, 210 | 'NMTOKENS' : XString, 211 | # binary 212 | 'hexBinary' : XString, 213 | 'base64Binary' : XString, 214 | # integers 215 | 'int' : XInteger, 216 | 'integer' : XInteger, 217 | 'unsignedInt' : XInteger, 218 | 'positiveInteger' : XInteger, 219 | 'negativeInteger' : XInteger, 220 | 'nonPositiveInteger' : XInteger, 221 | 'nonNegativeInteger' : XInteger, 222 | # longs 223 | 'long' : XLong, 224 | 'unsignedLong' : XLong, 225 | # shorts 226 | 'short' : XInteger, 227 | 'unsignedShort' : XInteger, 228 | 'byte' : XInteger, 229 | 'unsignedByte' : XInteger, 230 | # floats 231 | 'float' : XFloat, 232 | 'double' : XFloat, 233 | 'decimal' : XFloat, 234 | # dates & times 235 | 'date' : XDate, 236 | 'time' : XTime, 237 | 'dateTime': XDateTime, 238 | 'duration': XString, 239 | 'gYearMonth' : XString, 240 | 'gYear' : XString, 241 | 'gMonthDay' : XString, 242 | 'gDay' : XString, 243 | 'gMonth' : XString, 244 | # boolean 245 | 'boolean' : XBoolean, 246 | } 247 | 248 | @classmethod 249 | def maptag(cls, tag, fn): 250 | """ 251 | Map (override) tag => I{class} mapping. 252 | @param tag: An xsd tag name. 253 | @type tag: str 254 | @param fn: A function or class. 255 | @type fn: fn|class. 256 | """ 257 | cls.tags[tag] = fn 258 | 259 | @classmethod 260 | def create(cls, schema, name): 261 | """ 262 | Create an object based on the root tag name. 263 | @param schema: A schema object. 264 | @type schema: L{schema.Schema} 265 | @param name: The name. 266 | @type name: str 267 | @return: The created object. 268 | @rtype: L{XBuiltin} 269 | """ 270 | fn = cls.tags.get(name) 271 | if fn is not None: 272 | return fn(schema, name) 273 | else: 274 | return XBuiltin(schema, name) 275 | -------------------------------------------------------------------------------- /default/app.conf: -------------------------------------------------------------------------------- 1 | [ui] 2 | is_visible = false 3 | label = Add-on for JIRA 4 | 5 | [install] 6 | build = 1 7 | is_configured = true 8 | 9 | [package] 10 | id = jira 11 | check_for_updates = true 12 | 13 | [launcher] 14 | author = Fred de Boer, Russell Uman, Stephen Sorkin, Jeffrey Isenberg 15 | description = Add-on for JIRA 16 | version = 2.1 -------------------------------------------------------------------------------- /default/commands.conf: -------------------------------------------------------------------------------- 1 | [jira] 2 | filename = jira_rest.py 3 | local = true 4 | overrides_timeorder = true 5 | streaming = true 6 | supports_multivalues = true 7 | generating = stream 8 | 9 | [jirarest] 10 | filename = jira_rest.py 11 | local = true 12 | overrides_timeorder = true 13 | streaming = true 14 | supports_multivalues = true 15 | generating = stream 16 | 17 | [jirasoap] 18 | filename = jira_soap.py 19 | generating = true 20 | required_fields = 21 | 22 | # These streaming versions create real events. I'm not sure which is better yet. For now you get options. 23 | [jirasoapevents] 24 | filename = jira_soap.py 25 | generating=true 26 | local = true 27 | overrides_timeorder = true 28 | passauth = true 29 | required_fields = 30 | retainsevents = true 31 | 32 | [jiraxml] 33 | filename = jira_xml.py 34 | generating = true 35 | passauth = true 36 | required_fields = 37 | 38 | # These streaming versions create real events. I'm not sure which is better yet. For now you get options. 39 | [jiraxmlevents] 40 | filename = jira_xml.py 41 | generating=true 42 | local = true 43 | overrides_timeorder = true 44 | passauth = true 45 | required_fields = 46 | retainsevents = true -------------------------------------------------------------------------------- /default/jira.conf: -------------------------------------------------------------------------------- 1 | [jira] 2 | default_project = WTF 3 | tempMax = 1000 4 | 5 | ## SearchXML command configuration ## 6 | # Built-in fields to display. Required. 7 | # If you want to use one of the time options, include updated, created, and/or resolved 8 | # If you want a field that contains multiple values (e.g., lables), you need to include the path to the individual elements 9 | keys = link,project,key,summary,type,priority,status,resolution,assignee,reporter,created,updated,resolved,fixVersion,components,labels/label 10 | # Fields containing durations, force them to return seconds instead of something human-readable. Optional. 11 | time_keys = timeestimate, originalestimate, timespent 12 | # Custom fields to display. Optional. 13 | custom_keys = -------------------------------------------------------------------------------- /metadata/default.meta: -------------------------------------------------------------------------------- 1 | [] 2 | export=system --------------------------------------------------------------------------------