{ "info": { "author": "Mike Malinowski", "author_email": "mike@twisted.space", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python" ], "description": "# Overview\n\nXpf is an interface to perforce which offers the following benefits:\n\n* Pure python, no compiled dependencies (allow for use in Python 2, Python 3, Max, Maya & Motion Builder etc)\n\n* Failsafe calls - this module will not raise errors if the perforce server is in-accessible.\n\n* PyPi distribution making it easier for sharing tools with outsourcers\n\n\n# Installation\n\nEither download and place somewhere in your PYTHONPATH or install via pypi:\n\n```pip install xpf```\n\n\n# Why not P4Python\n\nPerforce offers its own p4Python module which is a well established and\nstable library.\n\nThe downside to P4Python is that its a compiled library and therefore requires\ndifferent distributions if you're jumping between Python 2.x and Python 3.x\n\nThis is further exasperated when wanting to utilise perforce integrations into\ntools built within embedded interpreters such as Autodesk Maya/Max/Motionbuilder\nwhich are all compiled against different compiler versions than the native\npython distributions.\n\nThe other benefit xpf brings is that its has a soft requirement on perforce.\nIts common place to want to embed perforce support into tools within\napplications such as Max/Maya but it opens the challenge of allowing your tools\nto still operate outside of your studio environments when the Perforce server\nis in-accessible.\n\nXpf resolves this by ensuring all the perforce calls can return default\nvariable types in circumstances where the Perforce server is unreachable. This\nallows for tools to operate outside the main studio environment, and is made\neasier by xpf being freely accessible on PyPi.\n\n\n## Xpf Direct\n\nThe library tries to make it easy to use for those that are used to utilising\nP4Python or the perforce command line. With that in mind you can utilise the\nthe ```xpf.direct``` module which mimics the types of calls and interface of\nP4Python.\n\nIn this regard you're given access to functions for each perforce command and\nare able to pass in any arguments you want to give. As per P4Python conventions\nyou will be returned a list of dictionaries representing the results.\n\nSome examples of this would be:\n\n```python\nimport xpf\n\n# -- Sync to the head revision of a directory\nxpf.direct.sync(\n [\n '/usr/my_files/...'\n ]\n)\n\n# -- Force sync, passing the usual perforce argument switches, but\n# -- define the client\nxpf.direct.sync(\n [\n '/usr/my_files/...'\n ],\n '-f',\n client='my_client_name',\n)\n```\n\nAs well as the defined functions (which are auto-generated) there is also a\nmore generic run function which you can utilise directly:\n\n```python\nimport xpf\n\n# -- Sync to the head revision of a directory\nresult = xpf.direct.run(\n 'describe',\n 13569, # -- Change list to describe\n 'Os', # -- Shorten output by excluding client workspace data\n 'Rc', # -- Limit output to files mapped into the current workspace.\n)\n```\n\nUsing ```xpf.direct``` should feel very familiar to anyone who has utilised\nP4Python or the perforce commandline.\n\n\n## Xpf Assist\n\nWorking at the ```xpf.direct``` level makes a lot of sense in a lot of\nsituations, however there are various circumstances which call for multiple\nqueries to be carried out in order to answer slightly higher level questions.\n\nExamples of these might be to add a file to a changelist regardless of whether\nits an add or edit operation. Another example might be where you want to manage\nthe changelist descriptions a little easier.\n\nThe ```xpf.assist``` module aims to give higher level functionality whereby the\nfunction in question will carry out multiple calls and wrangle data to solve\na particular request.\n\nExamples of these are:\n\n```python\nimport xpf\n\n# -- Given a chnagelist description, find the most likely changelist\n# -- number for the current user. In this case, if that changelist\n# -- does not exist it will be created for you\nresult = xpf.assist.get_changelist('My change description')\n```\n\nThe following example exposes a method of submitting which forces all\nfiles being submitted to be added to a changelist with the supplied\ndescription and submitted together:\n\n```python\nimport xpf\n\nxpf.assist.submit_files(\n [\n '/usr/my_files/...'\n ],\n description='My submission description',\n)\n```\n\n\n## Xpf Variables\n\nXpf works at a module level. It is not class based and it wraps the perforce\ncommand line. With this in mind it has some variables which are considered\nglobal, which are queried for on the first run (based on p4 set) but can be\naltered by you.\n\n```python\nimport xpf\n\n# -- Get the host\nhost = xpf.variables.get_host()\n\n# -- Set the host to something specific\nxpf.variables.set_host(host)\n```\n\nVariables which can be retrieved and set in this way include:\n\n * host\n * client\n * user\n * port (server)\n * timeout\n\nA special variable which can be turned on/off is the `debugging` variable.\nWhen debugging is turned on xpf will print ever command line its about to\nprocess in the final format its constructed in. This is particularly useful\nif you're getting a result you do not expect and want to recreate the steps\nusing the commandline.\n\nTo enable this option you do:\n\n```python\nimport xpf\n\nxpf.variables.set_debug(True)\n```\n\n\n## Failsafes\n\nOne of the big advantages of xpf is that includes in-built failsafe mechanisms\nto protect functionality whenever the server is in-accessible. During the first\nxpf call (regardless of whether that is through ```xpf.direct``` or\n```xpf.assist```) a ```p4 info``` query is run. If this timesout then the\nxpf variable is set to mark the server as inaccessible.\n\nWhen a server is inaccessible all functions will return a default value which\nis defined by their failsafe decorator. This allows your code to continue\nwithout having to handle server failure directly.\n\nIn many situations when calling functions within ```xpf.direct``` they will\nreturn an empty list upon server failure - this is because their functions\nusually return a list. With that in mind, whilst you dont have to handle server\nfailure you should handle being given empty data of the correct (expected)\ntype.\n\nA good example of this would be:\n\n```python\n\ndescription = xpf.direct.describe(13569)\n\nif not description:\n pass\n\n...\n```\n\nIn the example above we do have to handle not being given a description but\nwe do not have to handle unexpected exceptions beacuse of an inaccessible\nperforce server.\n\nFundamentally this means you can safely embed an xpf dependency in your tool\nto give a rich user experience knowing that the tool will work even if its taken\noff-site.\n\n\n## Timeouts\n\nXpf offers the ability to define a timeout on all perforce queries. By default\nthis timeout is exposed through ```xpf.set_timeout(value)``` and is defaulted\nto one second. If your server is particularly slow you can use that call to\nincrease the global timeout.\n\nAlternatively, you can set the timeout on a per-call basis too. This is\nparticularly useful when you know your call will take a longer than expected\ntime. This is done with the `timeout` argument as shown here:\n\n```python\nimport xpf\n\nxpf.direct.info(timeout=10)\n```\n\n\n## Marshalling\n\nBy default all queries run through xpf are marshaled, and therefore return\npythonic objects. If you want raw output (strings) rather than lists of\ndictionaries you can set the marshaling to false on a per call basis as\nshown here:\n\n```python\nstring_dump = xpf.direct.run('set', marshal=False) # -- (Equivalent to p4 set)\n```\n\n\n## Forms\n\nForms are used in perforce to deliver multiple pieces of user input. When\nrunning perforce through the commandline this typically pops open a text\neditor - which is not particularly useful when interacting with perforce\nvia a python library.\n\nTherefore all forms that are requested by a particular p4 command can be\ngiven in the form of a dictionary. This is shown here:\n\n```python\nimport xpf\n\nresult = xpf.direct.changelist(\n '-i',\n form={\n 'Change': 'new',\n 'Status': 'new',\n 'Description': 'My New Changelist Description',\n },\n)\n```\n\n\n# Compatibility\n\nXpf has been tested under Python 2.7 and Python 3.7 on Windows and Ubuntu.\n\nNote: This module utilises the ```p4 set``` environment variables and expects\nat least P4PORT, P4USER and P4CLIENT to be defined.", "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/mikemalinowski/xpf", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "xpf", "package_url": "https://pypi.org/project/xpf/", "platform": "", "project_url": "https://pypi.org/project/xpf/", "project_urls": { "Homepage": "https://github.com/mikemalinowski/xpf" }, "release_url": "https://pypi.org/project/xpf/1.0.1/", "requires_dist": null, "requires_python": "", "summary": "A python interface to interacting with p4 as a soft dependency", "version": "1.0.1" }, "last_serial": 5103832, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "5fb83f83b3cbb79b05c79a5208a9960b", "sha256": "b659fb2ce225f3a9d7d0de21be29de336e6149eec2a57bd87e459fde15a6e903" }, "downloads": -1, "filename": "xpf-1.0.0.tar.gz", "has_sig": false, "md5_digest": "5fb83f83b3cbb79b05c79a5208a9960b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17350, "upload_time": "2019-03-15T22:26:10", "url": "https://files.pythonhosted.org/packages/70/b3/e967318bcfeeaa6c2a59e9fb8ab3a705c894b7c638904129dd99f1ded1b9/xpf-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "753c7ebc6b7fd6fa833a2389497f793e", "sha256": "7d051d858e9da68a3bc9f8afa38ffc266825221bcfecb1b3263c30390b531a1a" }, "downloads": -1, "filename": "xpf-1.0.1.tar.gz", "has_sig": false, "md5_digest": "753c7ebc6b7fd6fa833a2389497f793e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16897, "upload_time": "2019-04-05T13:57:41", "url": "https://files.pythonhosted.org/packages/4a/13/76a2bbb56225fe6658878fbb5a79abd95a19e759a8b733f0d306df0c7210/xpf-1.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "753c7ebc6b7fd6fa833a2389497f793e", "sha256": "7d051d858e9da68a3bc9f8afa38ffc266825221bcfecb1b3263c30390b531a1a" }, "downloads": -1, "filename": "xpf-1.0.1.tar.gz", "has_sig": false, "md5_digest": "753c7ebc6b7fd6fa833a2389497f793e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16897, "upload_time": "2019-04-05T13:57:41", "url": "https://files.pythonhosted.org/packages/4a/13/76a2bbb56225fe6658878fbb5a79abd95a19e759a8b733f0d306df0c7210/xpf-1.0.1.tar.gz" } ] }