{ "info": { "author": "Aaron Caffrey", "author_email": "acaffrey@salesforce.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "# SalesforcePy\n\n[![Build Status](https://travis-ci.com/forcedotcom/SalesforcePy.svg?branch=master)](https://travis-ci.com/forcedotcom/SalesforcePy)\n[![Coverage Status](https://coveralls.io/repos/github/forcedotcom/SalesforcePy/badge.svg?branch=master)](https://coveralls.io/github/forcedotcom/SalesforcePy?branch=master)\n[![Documentation Status](https://readthedocs.org/projects/salesforcepy/badge/?version=latest)](https://salesforcepy.readthedocs.io/en/latest/?badge=latest)\n\nAn absurdly simple package for making Salesforce Rest API calls.\n\nby Aaron Caffrey, Colin Cheevers, Jose Garcia, Tania Prince\n\nSalesforce.com\n\n## Table of Contents\n- [Introduction](#introduction)\n- [Contributors](#contributors)\n- [Requirements](#requirements)\n- [Install](#install)\n- [Create a client and log in](#create-a-client-and-log-in)\n- [Query](#query)\n- [Query More](#query-more)\n- [Insert SObject](#insert-sobjects)\n- [Update SObject](#update-sobjects)\n- [Delete SObject](#delete-sobjects)\n- [Query SObject Row](#query-sobject-row)\n- [Describe SObject](#describe-sobject)\n- [Describe Global](#describe-global)\n- [Insert File](#insert-file)\n- [Search](#search)\n- [Execute Anonymous](#execute-anonymous)\n- [Approval Process](#approval-process)\n- [Chatter](#chatter)\n- [Wave](#wave)\n- [Bulk API 2.0](#bulk-api)\n- [Logout](#logout)\n- [Contributing](#contributing)\n- [FAQ](#faq)\n\n## Introduction\nThe reason this package exists is to produce:\n\n1. A Salesforce client that is reusable, minimalistic, and pythonic\n2. Interfaces that are closely knit to the Salesforce Rest API service specification\n3. Gradual support for the Salesforce API extended family (ie. Chatter, Analytics, Wave, Tooling, Bulk, Metadata, etc.)\n\n## Contributors\nThanks goes to the people who have contributed code to this module, see the\n[GitHub Contributors page][].\n\n[GitHub Contributors page]: https://github.com/forcedotcom/SalesforcePy/graphs/contributors\n\n## Requirements\n * Python 2 or 3\n\n## Install\n### From git\n```bash\npip install git+ssh://git@github.com/forcedotcom/SalesforcePy.git\n```\n\n### From local source\n1. Download and extract, or clone this repo\n2. `cd` into the `/SalesforcePy` directory\n3. Run the following:\n```bash\npip install .\n```\n\n## Create a client and log in\nGetting started is a three-step process:\n\n1. Import `SalesforcePy`\n2. Create a client\n3. Perform a login request\n\n```python\nimport SalesforcePy as sfdc\n\n# Create an instance of a Salesforce client, replacing the credentials below with valid ones.\nclient = sfdc.client(\n username=\"jsoap@universalcontainers.com\",\n password=\"p@ssword1\",\n client_id=\"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\",\n client_secret=\"123456789123456789\",\n login_url=\"test.salesforce.com\",\n version = \"38.0\", #optional parameter, defaults to the latest version for your instance\n timeout = \"30\", # optional, defines a connect/read timeout value, if not specified requests can hang for minutes or more.\n\n)\n\n# Log in\nlogin_results = client.login()\n```\n\nIn the example above, `login_results[0]` will be a dict with the response from the Salesforce OAuth resource. The only supported flow at present is username-password. For more on the response, see: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_understanding_username_password_oauth_flow.htm.\n\nYou can also use the context manager which handles `login()` and `logout()` automatically.\n\n```python\nclient_args = {'username' :'jsoap@universalcontainers.com',\n 'password' :'p@ssword1',\n 'client_id': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n 'client_secret' : '123456789123456789'\n }\n\nwith sfdc.client(**client_args) as client:\n\n search_result = client.search('FIND {\"test\"} RETURNING Case(Id)')\n\n```\n\n## Query\nOnce the login call has been performed successfully, the client will maintain the session leaving you free to make API calls. This example demonstrates how to perform a query.\n```python\nquery_results = client.query('SELECT Id, Name FROM Account LIMIT 1')\n```\n\nIn the example above `query_results[0]` will be a dict with the response as documented here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm.\n\n## Query More\nWhile the client [`query()`](#query) method can be useful for making requests when you're expecting a small amount of data, a single query result *won't* give you all records if the total size of records exceeds the standard batch size (2000).\n\nIn such scenarios, you may wish to use `query_more()`. As the example shows, you simply provide the query string in the same way you did previously with `query()`:\n```python\nquery_results = client.query_more('SELECT Id, Name FROM Account')\n```\n\nIn the example above `query_results[0]` will be a list of dicts, each of which is a query result (batch). The behaviour of `query_more` is to consume `\"nextRecordsUrl\"` of each query result recursively until it runs out. For more on this topic, check out https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm under the heading **\"Retrieving the Remaining SOQL Query Results\"**.\n\n## Insert SObjects\nFrom the client, the `sobjects` class can be used to perform DML statements and file i/o with Salesforce objects. This example shows how to insert.\n```python\ncreate_result = client.sobjects(object_type='Account').insert({\"Name\" : \"SalesforcePy\"})\n```\n\nIn the example above `create_result[0]` will be a dict with the response as documented here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm.\n\n## Update SObjects\nUpdate works similarly to insert, with the main difference being that `id` is a required kwarg in `sobjects` so it is clear which record is to be updated.\n```python\nupdate_result = client.sobjects(id='0010Y0000055YG7QAM',object_type='Account').update({\"Name\" : \"SalesforcePy 2\"})\n```\n\nIn the example above `update_result[0]` will be `None`. This is because the HTTP method used under the hood is `PATCH`, for which the expected success code is `204`. The success code can be found in `update_result[1].status`. For more, read: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm.\n\n## Delete SObjects\n```python\ndelete_result = client.sobjects(id='0010Y0000055YG7QAM',object_type='Account').delete()\n```\n\nIn the example above `delete_result[0]` will be `None`. This is because the HTTP method used under the hood is `DELETE`, for which the expected success code is `204`. The success code can be found in `delete_result[1].status`. For more, read: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_delete_record.htm.\n\n## Query SObject Row\nIf you know the ID of a record, you can easily retrieve the entire row using this method. This may be preferable to the `query` call documented further up if you wish to get all fields without specifying them.\n```python\nquery_result = client.sobjects( object_type=\"Account\", id=\"0010Y0000056ljcQAA\" ).query()\n```\n\n## Describe SObject\nThe describe method retrieves the individual metadata at all levels for the specified SObject\n```python\ndescribe_result = client.sobjects(object_type='Account').describe()\n```\n\nIn the example above `describe_result[0]` will be a dict with the response as documented here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm. The If-Modified-Since header cannot be used with this method.\n\n## Describe Global\nThe describe global method lists the available objects and their metadata for the organization\u2019s data. In addition, it provides the organization encoding, as well as the maximum batch size permitted in queries.\n```\ndescribe_global_result = client.sobjects().describe_global()\n```\n\nIn the example above `describe_global_result[0]` will be a dict with the response as documented here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_describeGlobal.htm. The If-Modified-Since header cannot be used with this method.\n\n## Insert File\nThere are some special objects in Salesforce such as `Attachment`, `Document`, and `ContentVersion` which allow storage of files as blobs. The following is an example of how to insert a file.\n```python\n# Create a file tuple ordered like so: ( filename, body, content_type )\nfile = ( \"SalesforcePy.txt\", \"Hello world\", \"text/plain\" )\n\ninsert_result = client.sobjects(object_type = \"Attachment\", binary_field=\"Body\").insert({\n \"Name\":\"SalesforcePy\",\n \"ParentId\":\"0010Y0000056ljcQAA\",\n \"Description\":\"An excellent package\"\n }, binary=file ) # Pass your file through using the binary kwarg\n```\n\n## Search\nSOSL search statements can be made like so:\n```python\nsearch_result = client.search('FIND {SalesforcePy} RETURNING Account(Id, Name) LIMIT 5')\n```\n\n## Execute Anonymous\nAnonymous Apex can be executed in a Salesforce organisation like so:\n```python\nea_result = client.execute_anonymous('system.debug(\\'Hello world.\\');')\n```\n\n## Approval Process\nApprovals can be retrieved, submitted and approved/rejected\n```python\nap_result = client.approvals(requestBody)\n```\nSee documentation for sample request body\n\n## Chatter\nCreate a feed item (chatter post). It returns a 201 status code for a successful request. See Chatter REST api\n documentation for information on the expected body to create feed items.\n\n```python\n# create chatter post\nclient.chatter.feed_item(body)\n\n# create a comment on a chatter post\nclient.chatter.feed_comment('feed-elementid', body)\n```\n\n## Wave\n### Retrieve a data set\nRetrieve a wave data set using the `datataset()` function.\n\n```python\nclient.wave.dataset(\"opportunities\")\n```\n\n### Perform a query\nPerform a SAQL query using the wave `query()` function.\n\n```python\nquery = {\n \"query\": \"\"\"q = load \\\"0Fb0N000000XuvBSAS/0Fc0N000001M5BMSA0\\\";\\nq = filter q by 'Account.Industry' in\n[\\\"Apparel\\\", \\\"Banking\\\", \\\"Biotechnology\\\"];\\nq = group q by 'Account.Industry';\\nq = foreach q generate\n'Account.Industry' as 'Account.Industry', count() as 'count';\\nq = order q by 'Account.Industry' asc;\\nq = limit q\n2000;\"\"\"\n}\n\nclient.wave.query(query)\n```\n\n## Bulk API 2.0\nAs a general rule, supported Bulk API 2.0 calls can be made from `client.jobs.ingest`. The samples below cover specific\ncalls.\n\n### Create a job\nIn this example, we create a job to insert accounts.\n\n```python\njob_resource = {\"object\": \"Account\", \"operation\": \"insert\", \"lineEnding\": \"CRLF\"}\n\nclient.jobs.ingest.create(job_resource=job_resource)\n```\n\nFor more information on the response for this request, see \nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/create_job.htm.\n\n### Upload job data\nIn this example, we create a job, then upload a csv file using the job ID.\n\n```python\njob_resource = {\"object\": \"Account\", \"operation\": \"insert\", \"lineEnding\": \"CRLF\"}\ncreate_result = client.jobs.ingest.create(job_resource=job_resource)\n\nwith open(\"/path/to/accounts.csv\") as f:\n csv_file = f.read()\n job_id = create_result[0].get(\"id\")\n batches_result = client.jobs.ingest.batches(job_id=job_id, csv_file=csv_file)\n```\n\nFor more information on the response for this request, see\nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/upload_job_data.htm.\n\n### Update a job state\nIn this example, we create a job, upload a csv file using its job ID, then update it with a state of `\"UploadComplete\"`.\n\n```python\njob_resource = {\"object\": \"Account\", \"operation\": \"insert\", \"lineEnding\": \"CRLF\"}\ncreate_result = client.jobs.ingest.create(job_resource=job_resource)\njob_id = create_result[0].get(\"Id\")\n\nwith open(\"/path/to/accounts.csv\") as f:\n csv_file = f.read()\n batches_result = client.jobs.ingest.batches(job_id=job_id, csv_file=csv_file)\n\nclient.jobs.ingest.update(job_id=job_id, state=\"UploadComplete\")\n```\n\nFor more information on the response for this request, see\nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/close_job.htm\n\n### Delete a job\nIn this example, we delete a job based on its ID. Assumed in this example that this value is stored in `job_id`.\n\n```python\ndelete_result = client.jobs.ingest.delete(job_id=job_id)\n```\n\nFor more information on the response for this request, see\nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/delete_job.htm\n\n### Get all jobs\nIn this example, we get a list of all jobs.\n\n```python\nget_result = client.jobs.ingest.get()\n```\n\nFor more information on the response for this request, see\nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/get_all_jobs.htm\n\n### Get job info\nIn this example, we get information for a specific job based on its ID. Assumed in this example that this value is\nstored in `job_id`.\n\n```python\nget_result = client.jobs.ingest.get(job_id=job_id)\n```\n\nFor more information on the response for this request, see\nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/get_job_info.htm\n\n### Get job successes\nIn this example, we get a successes CSV for a given job based on its ID. Assumed in this example that this value is\nstored in `job_id`.\n\n```python\nget_result = client.jobs.ingest.get(job_id=job_id, successes=True)\n```\n\nFor more information on the response for this request, see\nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/get_job_successful_results.htm\n\n### Get job failures\nIn this example, we get a failures CSV for a given job based on its ID. Assumed in this example that this value is\nstored in `job_id`.\n\n```python\nget_result = client.jobs.ingest.get(job_id=job_id, failures=True)\n```\n\nFor more information on the response for this request, see\nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/get_job_failed_results.htm\n\n### Get job unprocessed rows\nIn this example, we get an unprocessed rows CSV for a given job based on its ID. Assumed in this example that this value\nis stored in `job_id`.\n\n```python\nget_result = client.jobs.ingest.get(job_id=job_id, unprocessed=True)\n```\n\nFor more information on the response for this request, see\nhttps://developer.salesforce.com/docs/atlas.en-us.api_bulk_v2.meta/api_bulk_v2/get_job_unprocessed_results.htm\n\n## Logout\nExpires the session by revoking the access token. It returns a 200 status code for a successful token revocation.\n```python\nclient.logout()\n```\n\n## Contributing\n### What's the git workflow?\n 1. Fork this repo\n 2. `git clone -b `\n 3. From within the project root directory run `pip install .`\n 4. Develop\n 5. Cover your code in `/tests`\n 6. Create a pull request to the `forcedotcom:developer` branch\n\n### How to format code\nWe recommend following the Style Guide for Python the best you can: https://www.python.org/dev/peps/pep-0008/.\n\n`autopep8` is a great tool for automatic formatting, we encourage its use: https://pypi.python.org/pypi/autopep8.\n\n## FAQ\n### I need to inspect my organisation schema. What's an easy way to do this?\n1. Log in to Workbench: https://workbench.developerforce.com/login.php\n2. Go to Info > Standard and Custom Objects\n3. In the Object dropdown, choose the object you wish to inspect (eg. Case) then click Select\n4. Expand Fields. You should find what you're looking for here.\n\n### I need to test a query. What's an easy way to do this?\n1. Log in to Workbench: https://workbench.developerforce.com/login.php\n2. Go to Queries > SOQL Query\n3. Enter your query or optionally use the form to help you build the query, then click Query\n\n### Is it possible to debug requests being made by `SalesforcePy`?\nYes. Here's an example of how to do it, and what to expect.\n\n```python\nimport logging\nimport SalesforcePy as sfdc\n\nusername = \"jsoap@universalcontainers.com\"\npassword = \"p@ssword1\"\nclient_id = \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\nclient_secret = \"123456789123456789\"\n\nclient = sfdc.client(\n username=username,\n password=password,\n client_id=client_id,\n client_secret=client_secret\n)\nclient.debug(level=logging.INFO) # Tell the client to debug at an info level\nclient.login() # Outputs \"POST https://login.salesforce.com/services/oauth2/token\" to logs\n```\n\n### I need a proxy to talk to Salesforce orgs. Can I specify this in the code?\nYes. Here's an example of how to do it.\n\n```python\nimport SalesforcePy as sfdc\n\nusername = \"jsoap@universalcontainers.com\"\npassword = \"p@ssword1\"\nclient_id = \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\nclient_secret = \"123456789123456789\"\n\nclient = sfdc.client(\n username=username,\n password=password,\n client_id=client_id,\n client_secret=client_secret,\n proxies={\"https\": \"localhost:8888/example/\"} # `proxies` kwarg takes a dict as required by the `requests` module.\n)\n```\n\n## Advanced Usage\n### Using keyword arguments\nSome of the parameters that are optionally defined at the client level can be defined at the function level\nas well. Function level arguments supersede the client arguments.\n\nFor example, you may want to define an overall timeout value of `\"30\"` for all requests but specify a higher\nvalue for query calls.\n```python\nclient = sfdc.client(\n username=username,\n password=password,\n client_id=client_id,\n client_secret=client_secret,\n timeout=\"30\"\n)\nquery_kwarg= {\"timeout\" : \"60\"}\nclient.query(\"Select Id FROM Account\",**query_kwarg)\n```\n\nThe following parameters support function level overriding:\n- `proxies`\n- `timeout`\n- `version`", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/forcedotcom/SalesforcePy", "keywords": "", "license": "BSD", "maintainer": "", "maintainer_email": "", "name": "SalesforcePy", "package_url": "https://pypi.org/project/SalesforcePy/", "platform": "", "project_url": "https://pypi.org/project/SalesforcePy/", "project_urls": { "Homepage": "https://github.com/forcedotcom/SalesforcePy" }, "release_url": "https://pypi.org/project/SalesforcePy/1.1.1/", "requires_dist": null, "requires_python": "", "summary": "An absurdly simple package for making Salesforce Rest API requests", "version": "1.1.1" }, "last_serial": 5182654, "releases": { "1.0.1": [ { "comment_text": "", "digests": { "md5": "9932db5461aab3c66680b47f932a0b91", "sha256": "3cbd80e46f57887213c4982984b04790d754687184dfdb31ea7dda1aaf25960b" }, "downloads": -1, "filename": "SalesforcePy-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "9932db5461aab3c66680b47f932a0b91", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 19360, "upload_time": "2018-10-12T15:06:52", "url": "https://files.pythonhosted.org/packages/f3/a2/b38df15c004c1c7a36cd5925e41e5fb6247e0744ef47dd9ec874462f23b7/SalesforcePy-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "14d296562958b722c64766f3c2f43192", "sha256": "e416fa8fcea2d47ccd3575fe5b45948de1b4b966399d827a8838e7d5212eecd3" }, "downloads": -1, "filename": "SalesforcePy-1.0.1.tar.gz", "has_sig": false, "md5_digest": "14d296562958b722c64766f3c2f43192", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17040, "upload_time": "2018-10-12T15:06:54", "url": "https://files.pythonhosted.org/packages/27/9f/33ce43f04dd1da5b8638ff88b75e1ac53bf888ab542f3fcb5f3f7e789249/SalesforcePy-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "7f1a6c9bd21279aabc36510276641726", "sha256": "02b53b278cf1518664e00495d226dacc9289db4e5bb0cb086cdf7083b3624709" }, "downloads": -1, "filename": "SalesforcePy-1.0.2.tar.gz", "has_sig": false, "md5_digest": "7f1a6c9bd21279aabc36510276641726", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17574, "upload_time": "2019-04-24T14:32:11", "url": "https://files.pythonhosted.org/packages/db/19/cb3d6bf200608b776beeb0dd4f4234e9d8a5112f986b967f6e35c5497f4e/SalesforcePy-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "3cec030e6fdc0c210e629d938c828ac9", "sha256": "f68fe3c1d3c3b9006d52126b50715d9d903d023ddb6fba2cb7cbf177572c2a85" }, "downloads": -1, "filename": "SalesforcePy-1.0.3.tar.gz", "has_sig": false, "md5_digest": "3cec030e6fdc0c210e629d938c828ac9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17687, "upload_time": "2019-04-24T14:34:10", "url": "https://files.pythonhosted.org/packages/ad/92/5a8683800812afbc4d37d29dadb0a16e74217c17e97bb3fcfb9955e69b96/SalesforcePy-1.0.3.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "329e3aa04338b6548d72326402de8d41", "sha256": "9db3e137a22b7120ce47be40b8c7f09b1a1a0ce433f2f5c645f08188669f686a" }, "downloads": -1, "filename": "SalesforcePy-1.1.0.tar.gz", "has_sig": false, "md5_digest": "329e3aa04338b6548d72326402de8d41", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19849, "upload_time": "2019-04-24T14:36:22", "url": "https://files.pythonhosted.org/packages/cb/20/f0a34ff363ea71d3ef663c1746e98caea05df964780299e89ea5f385112c/SalesforcePy-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "59e6805c027f4ab6467e9955928f8155", "sha256": "8e8f10415c34d6f8bb933c57ac31ccf9486f79d257159d50aaedc7ce37610f74" }, "downloads": -1, "filename": "SalesforcePy-1.1.1.tar.gz", "has_sig": false, "md5_digest": "59e6805c027f4ab6467e9955928f8155", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19843, "upload_time": "2019-04-24T14:37:34", "url": "https://files.pythonhosted.org/packages/26/6f/54e7b35c99241eca1b3e5719eed09290ef31a3759221d4758948c56d77ee/SalesforcePy-1.1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "59e6805c027f4ab6467e9955928f8155", "sha256": "8e8f10415c34d6f8bb933c57ac31ccf9486f79d257159d50aaedc7ce37610f74" }, "downloads": -1, "filename": "SalesforcePy-1.1.1.tar.gz", "has_sig": false, "md5_digest": "59e6805c027f4ab6467e9955928f8155", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19843, "upload_time": "2019-04-24T14:37:34", "url": "https://files.pythonhosted.org/packages/26/6f/54e7b35c99241eca1b3e5719eed09290ef31a3759221d4758948c56d77ee/SalesforcePy-1.1.1.tar.gz" } ] }