-
-
-
-
It might be better to have a list of SCMCLIENTS (or a dict, if you want to keep the keys) instead of writing this out statically. The original postreview code did this, and it seemed to work pretty well.
-
import RBUtilities should be put after import os. We do imports alphabetically. If there's an unordered list (like imports, or css attributes), we *tend* to put things in alphabetical order.
-
-
If you're outside of a class, there are two lines between code blocks. So we'd insert 2 lines between the imports and the try/catch.
-
-
-
-
-
-
-
-
Starting point for the Python API
Review Request #1847 — Created Oct. 19, 2010 and discarded
Starting point for the Python API, created by myself and Brendan Curran-Johnson. The project is divided into three parts, API, Clients, and Commands. The API deals with resources and RESTful communications to a reviewboard server. Also, it deals with OS calls and generic configuration data. The Clients deal specifically with the different SCMs available to be used with reviewboard. In a nutshell - generating diffs and grabbing repository information. The Commands are a set of scripts which use the API. Most of the functionality found in post-review can be found in the current commands.
Able to run the scripts: rb-close (close a review request as submitted or discarded) rb-config (configure the server_info for rb_scripts) rb-create (create a new review request) rb-get-diff (generate a diff) rb-info (get the information for any base resource list or base resource list's child) rb-open (open a review request) rb-publish (publish a review request) rb-upload (upload a diff or screenshot to a review request)
-
Hi guys, This is going to be a long review. You knew that was coming, but don't worry :) There are a lot of stylistic things that still need a lot of love. I listed some examples of this but I didn't go through every line to comment on each occurrence. Please go through your code and make sure that everything follows the styles as expected. This will make it *much* easier to review logic and implementation rather than style. As for the structure of the code, I really want to see different types of classes segmented into namespaces. There should be the following: * rbtools.api -- All API-related things should go somewhere in here. Resources, for instance, should go in rbtools.api.resources (or rbtools/api/resources.py). * rbtools.clients -- All support for clients (git, svn, etc.) should go in here. This is what you currently have as the repository classes. I want to see one file per type of repository. So, rbtools.clients.git (or rbtools/clients/git.py) would have GitRepository and GitClient. * rbtools.commands -- All scripts would move into here, and you'd use the Python setuptools scripting stuff to wrap these. See RBTool's setup.py for how this is done with post-review. On that note, filenames should always be lowercase in Python. The mixed-case style of Java, C++, etc. isn't used here. I want to see some changes to the way Resources are done. Basically, I want to be able to treat a Resource as a sort of structure I can get/create, populate, and save, and have it work sort of like the Django database API. Something like: review_request_list = ... # Pretend we have this at this point review_request = review_request_list.create() review_request.submit_as = some_user review_request.repository = repository_id review_request.save() In a way, it's like the Django database API. I should also be able to fetch a resource, change some stuff on it, and save(). If the review request is newly created, do an HTTP POST. If it's one fetched from a server, do a PUT. Should also provide a delete() method. Etc. So, basically, there would be two main types of resources: Lists, and objects. List resources would have create() and get() methods. create() would create a new instance of the type of child resource, which could be populated and saved. I'd like a get() that would get the correct resource from the list. The only way this can be reliably done (since an object doesn't necessarily live in that resource, as it may be linked to another place) is to query the root resource (which should only be done once in the lifecycle of the app), find the uri_template for the resource name (say, 'review_requests'), and plug values into the URI template (review_request_id, etc.). Those values should come from values passed to get() and from the list resource's values (and its parents, recursively up the chain). So, I could do 'review_request_list.get(42)' and it would plug 42 into review_request_id. It would know this from, say, an 'uri_id_field' key in the resource subclass. It would get any info from the list, its parent, etc., and then once it has all this, parse the URI template and get the URL. Then fetch that and return the resource type. Resource lists would also need to be able to return their results in a nice, Pythonic way involving iterators. I want to be able to do: for resource in resource_list: ... and: resources = resource_list[4:10] Resource objects would have save() and delete() methods. Fairly self-explanatory I think. For all this to work, you need two classes (they should have a base parent class that has most of the logic for this, though), and then a subclass for each thing that we care about. ResourceList subclasses would probably need: * child_resource_type (reference to a class, such as ReviewRequest). * create() * to be iterable (there should be lots of examples of this on the web). Resource subclasses would need: * uri_id_field * resource_name * save() * delete() That's probably a lot to process. Feel free to discuss it further over e-mail on reviewboard-dev@.
-
Imports are always in three groups, each separated by a blank line: 1) System imports (such as sys, mimetools, socket, etc.) 2) Third-party imports (would be things such as djblets, though that's not needed here). 3) Local imports (in this case, Repository). For local imports, use the full import path. In this case, 'from rbtools import Repository'.
-
-
-
-
Functions should always be more like: def __init__(self, log_file='rbproblems.log'): Note the lack of spaces within the parens, and the lack of spaces around the '='. This is true only for keyword arguments, not general assignment in code.
-
Docs in Python shouldn't contain the function header (the __init__(...)) or list of parameters. The very first line should always be the summary, like: """Briefly describes the function. More detail that can span paragraphs here. """ Though, for init functions, don't bother with docs.
-
-
-
Don't define a long list of things in the header. This should go outside of the function somewhere. Always wrap to < 80 chars.
-
It would be better to have a lookup map somewhere outside the function, like: repository_types = { 'svn': Repository.SVNRepository, 'cvs': Repository.CVSRepository, } Then you can do: if type in repository_types: possible_repos.append(repository_types[tpe](url, self)) else: self.raise_warning(...)
-
-
-
-
You should never need this. It's deprecated. All string operations can be performed on strings themselves.
-
-
-
-
-
Actually, do this instead: .. note:: This method MUST blah blah blah blah It'll appear nicer in generated docs.
-
-
-
You can make a lot of this much easier. Require that subclasses define the name of the resource field within, like so: class ReviewRequest(Resource): resource_name = 'review_request' Then have Resource be smart enough to do the get_field. Rather than things like draft_url, just provide an easy way to get a named URL. I think having get_link jut take the name and return the href instead of requiring the ['href'] would do that nicely.
-
Align these, and no need for \. Also. there's an easier way here. Define this as: def __init__(self, method, *args, **kwargs): Then, for the parent constructor call, do: super(RequestWithMethod, self).__init__(self, *args, **kwargs) That way, you never have to keep the params in sync with urllib2.Request.
-
-
-
-
-
-
-
-
-
Paths should never be hard-coded with the REST API. They should be "discovered" or "browsed" from the root resource.
-
-
-
These methods (the ones operating on review requests and such) don't really belong in here. The act of creating or updating an object should happen solely on the Resource for it.
-
Multi-line if statements should have the conditional wrapped in parens instead of using \. In this case, you can do: if not isinstance(self.current, (Resource.ReviewRequest, Resource.DraftReviewRequest):
-
More stylistic stuff, and some code simplification/reorganization. Remember, the filenames should be all lowercase, not mixed case. There are still things from previous reviews that I think are unfixed. When I'm modifying my change based on review feedback, I'll usually respond to each item with "Fixed" or something, so that the reviewers and I all know what was done and what wasn't.
-
Going to want David's take on this, but I generally would say that you should have a base ResourceError exception, and then simple subclasses for each error type, instead of error codes. So: class ResourceError(Exception): ... class UnloadedResourceError(ResourceError): pass etc. Also, any exceptions should go in errors.py.
-
-
Instructions probably don't belong in here. I'd do: return self.resource_string or "Unloaded resource"
-
First line of any doc string should be a one-sentence summary, ideally fitting on one line. It should be on the same line as the starting """. Then, a blank line, then the remaining docs.
-
Should be consistent with where the space is on multi-line strings. I always put it before the end quote mark. Also, any time you're breaking apart a string, all parts should be on the same indentation level. That includes "The resource"
-
-
I'm not convinced this is safe. You're using field both as the result and the thing you're fetching from. So what happens after the first iteration?
-
-
-
-
-
Shouldn't print. This may be used from non-console apps. Callers should be able to get something meaningful so they can handle it their own way.
-
Should use: super(ResourceBase, self).__init__(server_interface, ...) You'll notice that's different from what I said to do about the Exception above. The reason is that new-style objects (those inheriting from 'object' somewhere in the chain) can use super(). Exception, however, isn't a new-style object on older versions of Python that we support.
-
-
-
-
The ".. note:" is only for doc strings. Just use "NOTE: " here Also, this seems very inconsistent with the other functions. We should be consistent, one way or another. The fewer types of exceptions the caller has to care about, though, the better.
-
You should make url_field_ids set to [] by default, and then just append the get_field('id') to the list always.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
There are two ways to close a review request -- to mark it submitted, and to discard it. You should define constants for PENDING, SUBMITTED, CLOSED, and use those. They can be defined to "pending", "submitted", "closed", but that way strings aren't assumed to be used.
-
-
-
-
-
-
-
-
-
-
-
Should do '%s{some_id_field}%s' % (temp[0], temp[2]) I don't really know what all this is for, though.
-
-
-
-
-
-
-
-
-
-
-
Since the root resource is special, it should probably just provide special versions of these iterator functions, instead of having every resource checking if it's the root.
-
-
-
-
-
I'm really not sure what any of this is supposed to do. This would be where a nice explanatory comment would be helpful. My gut is that there's an easier way to write it, but a comment would help me figure that out :)
-
-
-
Sorry to make you kill off this function, but Python has what you probably need here. Python's 're' module (regular expressions) lets you do pattern-based splitting easily. You can do: import re left, center, right = re.split('{([^}]+)}', 'foo{abc}def') The pattern there matches something starting with { and anything up to (but not including) the }. Result in that case would be: ['foo', 'abc', 'def']
-
Ah, this is what I was wondering before. Isn't data a dictionary? If so, you can just do: return 'total_results' in data
-
When importing, you want to first have a group of built-in Python modules, then a blank line, then 3rd party modules + blank line (if any -- none in this case), then imports from this module (in this case, the rbtools ones).
-
-
-
This should probably be split off into some little utility thing in the rbtools.commands API. A graphical third-party app would never want the library prompting for input.
-
-
-
This doesn't have to be done in the first change, but it really shouldn't hard-code the names. What it should be doing is looking for a program with the name of sys.argv[1] with 'rb-' prepended. That way, third parties can make their own and it'll Just Work. But again, that's a future task, which isn't a priority over getting this change in.
-
-
-
-
- Change Summary:
-
Updates made based off the most recent review from Christian. In summary, what was done was: -Made "rb command" easily extensible. Now simply write the script named in the form "rb-<script name>.py" and add the name to __all__ in rbtools/commands/__init__.py. -Added the class RootResource which specifically deals with the root resource. -ServerInterface now uses RBv1.5 Authentication - Basic Authentication. -Moved all exceptions into errors.py. -Other miscellaneous style/efficiency changes.
- Description:
-
~ Starting point for the Python API, created by myself and Brendan Curran-Johnson.
~ Starting point for the Python API, created by myself and Brendan Curran-Johnson. The project is divided into three parts, API, Clients, and Commands.
~ The class intended to provide most of the API is ServerManager.
~ The API deals with resources and RESTful communications to a reviewboard server. Also, it deals with OS calls and generic configuration data.
+ + The Clients deal specifically with the different SCMs available to be used with reviewboard. In a nutshell - generating diffs and grabbing repository information.
+ + The Commands are a set of scripts which use the API. Most of the functionality found in post-review can be found in the current commands.
- Testing Done:
-
~ Able to run the scripts rb-create.py and rb-publish.py.
~ Able to run the scripts:
+ rb-close (close a review request as submitted or discarded) + rb-config (configure the server_info for rb_scripts) + rb-create (create a new review request) + rb-get-diff (generate a diff) + rb-info (get the information for any base resource list or base resource list's child) + rb-open (open a review request) + rb-publish (publish a review request) + rb-upload (upload a diff or screenshot to a review request)