Is there a lint rule for creating python tuples without bracets? - python

I want linter to restrict me from using tuple by mistakingly adding a comma at the end of an assignment.
I think that explicitly creating a tuple by using brackets is the only right way to create them.
I have tried 'Pylint' with option '--enable=all', it doesn't warn me of the danger.
# this looks bad and probably was added by mistake
a = 'abc',
# this immediately informs the reader that this is a tuple
# and for sure was added deliberately
b = ('abc',)
I look for a rule for any python linter that would return a warning for the first assignment but not for the second one.
If you have any tips for looking for rules for linters they would be much appreciated.

I have found a tool that does that and even more and becomes standard: Black: https://github.com/psf/black.

Related

Why does Pylint object to single-character variable names?

I'm still getting used to Python conventions and using Pylint to make my code more Pythonic, but I'm puzzled by the fact that Pylint doesn't like single character variable names. I have a few loops like this:
for x in x_values:
my_list.append(x)
and when I run pylint, I'm getting Invalid name "x" for type variable (should match [a-z_][a-z0-9_]{2,30} -- that suggests that a valid variable name must be between 3 and 31 characters long, but I've looked through the PEP8 naming conventions and I don't see anything explicit regarding single lower case letters, and I do see a lot of examples that use them.
Is there something I'm missing in PEP8 or is this a standard that is unique to Pylint?
A little more detail on what gurney alex noted: you can tell Pylint to make exceptions for variable names which (you pinky swear) are perfectly clear even though less than three characters. Find in or add to your pylintrc file, under the [FORMAT] header:
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_,pk,x,y
Here pk (for the primary key), x, and y are variable names I've added.
Pylint checks not only PEP8 recommendations. It has also its own recommendations, one of which is that a variable name should be descriptive and not too short.
You can use this to avoid such short names:
my_list.extend(x_values)
Or tweak Pylint's configuration to tell Pylint what variable name are good.
In explicitly typed languages, one-letter name variables can be ok-ish, because you generally get the type next to the name in the declaration of the variable or in the function / method prototype:
bool check_modality(string a, Mode b, OptionList c) {
ModalityChecker v = build_checker(a, b);
return v.check_option(c);
}
In Python, you don't get this information, so if you write:
def check_modality(a, b, c):
v = build_checker(a, b)
return v.check_option(c)
you're leaving absolutely no clue for the maintenance team as to what the function could be doing, and how it is called, and what it returns. So in Python, you tend to use descriptive names:
def check_modality(name, mode, option_list):
checker = build_checker(name, mode)
return checker.check_option(option_list)
And you even add a docstring explaining what the stuff does and what types are expected.
Nowadays there is also an option to override regexp. I.e., if you want to allow single characters as variables:
pylint --variable-rgx="[a-z0-9_]{1,30}$" <filename>
So, Pylint will match PEP8 and will not bring additional violations on top. Also you can add it to .pylintrc.
The deeper reason is that you may remember what you intended a, b, c, x, y, and z to mean when you wrote your code, but when others read it, or even when you come back to your code, the code becomes much more readable when you give it a semantic name. We're not writing stuff once on a chalkboard and then erasing it. We're writing code that might stick around for a decade or more, and be read many, many times.
Use semantic names. Semantic names I've used have been like ratio, denominator, obj_generator, path, etc. It may take an extra second or two to type them out, but the time you save trying to figure out what you wrote even half an hour from then is well worth it.
pylint now has good-names-rgxs, which adds additional regex patterns to allow for variable names.
The difference is that that variable-rgx will override any previous rules, whereas good-names-rgxs adds rules on top of existing rules. That makes it more flexible, because you don't need to worry about breaking previous rules.
Just add this line to pylintrc to allow 1 or 2 length variable names:
good-names-rgxs=^[_a-z][_a-z0-9]?$
^ # starts with
[_a-z] # 1st character required
[_a-z0-9]? # 2nd character optional
$ # ends with
The configuration that pylint itself generates will allow i, j, k, ex, Run, though not x,y,z.
The general solution is to adjust your .pylintrc, either for your account ($HOME/.pylintrc) or your project (<project>/.pylintrc).
First install pylint, perhaps inside your .env:
source myenv/bin/activate
pip install pylint
Next run pylint, but the file is too long to maintain manually right from the start, and so save on the side:
pylint --generate-rcfile > ~/.pylintrc--full
Look at the generated ~/.pylintrc--full. One block will say:
[BASIC]
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_
Adjust this block as you like (adding x,y,..), along with any other blocks, and copy your selected excerpts into ~/.pylintrc (or <project>/.pylintrc).

Word delimitors for python coding style?

PEP8 use _ as a word delimitor, for example, a method named get_context_data.
caw is so cool when you want change get_context_data to set_context_data.
I googled and added set iskeyword-=_ to my vimrc.
But another problem comes with this change, I can autocomplete the method name when I type get_, menu shows me context not context_data I want, is there a way to solve the problem?
By changing the 'iskeyword' setting, you influence (and potentially break) a lot of things; among them word motions (w, e, etc.), completions (your problem here), and syntax highlighting.
I recommend keeping the original setting (after all, get_context_data as a single variable should probably also be represented by a single word). You can use my camelcasemotion plugin to work on the underscore-separated fragments. With the plugin, you can either override the original motions and text objects, or use ca,w instead of caw.
Better don't change the iskeyword option. There are alot of commands need it. If you just want to make editing underscore connected string (a_b_c_d) easier, you could try this mapping:
onoremap iu :<c-u>normal! T_vt_<cr>
onoremap au :<c-u>normal! F_vf_<cr>
with this mapping, (iu means In Underscore, you can change it). you could for example
get_cont[I]ext_data ( [I]:cursor)
you type ciu you got get_[I]_data , diu will do delete, au will do operation also on wrapped underscores.
But, for these cases, the above mapping does not work (or work unexpectedly ^_*)
g[I]et_context_data -> [I]_context_data (you could do bct_ instead)
get_context_dat[I]a -> get_context_[I] (you could do ecT_ or T_cw instead)
Because the mapping doesn't work wordwise .you can make it suit your needs however.

Python - using a string in for in statement?

so i know this is a bit of a workaround and theres probably a better way to do this, but heres the deal. Ive simplified the code from where tis gathering this info from and just given solid values.
curSel = nuke.selectedNodes()
knobToChange = "label"
codeIn = "[value in]"
kcPrefix = "x"
kcStart = "['"
kcEnd = "']"
changerString = kcPrefix+kcStart+knobToChange+kcEnd
for x in curSel:
changerString.setValue(codeIn)
But i get the error i figured i would - which is that a string has no attribute "setValue"
its because if i just type x['label'] instead of changerString, it works, but even though changer string says the exact same thing, its being read as a string instead of code.
Any ideas?
It looks like you're looking for something to evaluate the string into a python object based on your current namespace. One way to do that would be to use the globals dictionary:
globals()['x']['label'].setValue(...)
In other words, globals()['x']['label'] is the same thing as x['label'].
Or to spell it out explicitly for your case:
globals()[kcPrefix][knobToChange].setValue(codeIn)
Others might suggest eval:
eval('x["label"]').setValue(...) #insecure and inefficient
but globals is definitely a better idea here.
Finally, usually when you want to do something like this, you're better off using a dictionary or some other sort of data structure in the first place to keep your data more organized
Righto, there's two things you're falling afoul of. Firstly, in your original code where you are trying to do the setValue() call on a string you're right in that it won't work. Ideally use one of the two calls (x.knob('name_of_the_knob') or x['name_of_the_knob'], whichever is consistent with your project/facility/personal style) to get and set the value of the knob object.
From the comments, your code would look like this (my comments added for other people who aren't quite as au fait with Nuke):
# select all the nodes
curSel = nuke.selectedNodes()
# nuke.thisNode() returns the script's context
# i.e. the node from which the script was invoked
knobToChange = nuke.thisNode()['knobname'].getValue()
codeIn = nuke.thisNode()['codeinput'].getValue()
for x in curSel:
x.knob(knobToChange).setValue(codeIn)
Using this sample UI with the values in the two fields as shown and the button firing off the script...
...this code is going to give you an error message of 'Nothing is named "foo"' when you execute it because the .getValue() call is actually returning you the evaluated result of the knob - which is the error message as it tries to execute the TCL [value foo], and finds that there isn't any object named foo.
What you should ideally do is instead invoke .toScript() which returns the raw text.
# select all the nodes
curSel = nuke.selectedNodes()
# nuke.thisNode() returns the script's context
# i.e. the node from which the script was invoked
knobToChange = nuke.thisNode()['knobname'].toScript()
codeIn = nuke.thisNode()['codeinput'].toScript()
for x in curSel:
x.knob(knobToChange).setValue(codeIn)
You can sidestep this problem as you've noted by building up a string, adding in square brackets etc etc as per your original code, but yes, it's a pain, a maintenance nightmare, and starting to go down that route of building objects up from strings (which #mgilson explains how to do in both a globals() or eval() method)
For those who haven't had the joy of working with Nuke, here's a small screencap that may (or may not..) provide more context:

PEP8, locals() and interpolation

Here is some code:
foo = "Bears"
"Lions, Tigers and %(foo)s" % locals()
My PEP8 linter (SublimeLinter) complains about this, because foo is "unreferenced". My question is whether PEP8 should count this type of string interpolation as "referenced", or if there is a good reason to consider this "bad style".
Well, it isn't referenced. The part that's questionable style is using locals() to access variables instead of just accessing them by name. See this previous question for why that's a dubious idea. It's not a terrible thing, but it's not good style for a program that you want to maintain in the long term.
Edit: It's true that when you use a literal format string, it seems more explicit. But part of the point of the previous post is that in a larger program, you will probably wind up not using a literal format string. If it's a small program and you don't care, go ahead and use it. But warning about things that are likely to cause maintainability problems later is also part of what style guides and linters are for.
Also, locals isn't a canonical representation of names that are explicitly referenced in the literal. It's a canonical representation of all names in the local namespace. You can still do it if you like, but it's basically a loose/sloppy alternative to explicitly using the names you're using, which is again, exactly the sort of thing linters are supposed to warn you about.
Even if you reject BrenBarn's argument that foo isn't referenced, if you accept the argument that passing locals() in string formatting should be flagged, it may not be worth writing to code to consider foo referenced.
First, in every case where that extra code would help, the construct is not acceptable anyway, and the user is going to have to ignore a lint warning anyway. Yes, there is some harm in giving the user two lint warnings to ignore when there's only actually one problem, especially if one of the warnings is somewhat misleading. But is it enough harm to justify writing very complicated code and introduce new bugs into the linter?
You also have to consider that for this to actually work, the linter has to recognize not just % formatting, but also {} formatting, and every other kind of string formatting, HTML templating, etc. that the user could be using. In practice, this means handling various very common forms, and providing some kind of hook for the user to describe anything else.
And, on top of that, even if you don't think it should work with arbitrarily-generated format strings, it surely has to at least work with l10n. How is that going to work? If the format string is generated by something like gettext, the linter has no way of knowing whether foo is referenced, unless it can check all of the translations and see that at least one of them references foo—which means it has to understand (or have hooks to be taught) every string translation mechanism, and have access to the translation database.
So, I would suggest that, even if you consider the warning spurious in this case, you leave it there anyway. At most, add something which qualifies the warning:
foo is possibly unreferenced, but in a function that uses locals()
The following wouldn't make SublimeLinter happy either, It looks up each variable name referenced in the string and substitutes the corresponding value from the namespace mapping, which defaults to the caller's locals. As such it show the inherent limitation a utility like SublimeLinter has when trying to determine if something has been referenced in Python). My advice is just ignore SublimeLinter or add code to fake it out, like foo = foo. I've had to do something like the latter to get rid of C compiler warnings about things which were both legal and intended.
import re
import sys
SUB_RE = re.compile(r"%\((.*?)\)s")
def local_vars_subst(s, namespace=None):
if namespace is None:
namespace = sys._getframe(1).f_locals
def repl(matchobj):
var = matchobj.group(1).strip()
try:
retval = namespace[var]
except KeyError:
retval = "<undefined>"
return retval
return SUB_RE.sub(repl, s)
foo = "Bears"
print local_vars_subst("Lions, Tigers and %(foo)s")

In python, is there a "pass" equivalent for a variable assignment

I am using a library function called get_count_and_price which returns a 2-tuple (count,price). In many places I use both time and price. However, in some I only need time or price. So right now, if I only need count, I assign to (count,price) and leave the price unused.
This works great and causes no trouble in and of itself.
However...
I use Eclipse with PyDev, and the new version 1.5 automatically shows errors and warnings. One of the warnings it shows is unused variables. In the above example, it flags price as unused. This is the sort of behavior which is great and I really appreciate PyDev doing this for me. However, I would like to skip the assignment to price altogether. Ideally, I would like something like:
(count,None) = get_count_and_price()
Now as we all know, None cannot be assigned to. Is there something else I could do in this case?
I know I could do something like
count = get_count_and_price()[0]
but I am asking just to see if anyone has any better suggestions.
I think there's nothing wrong with using the [0] subscript, but sometimes people use the "throwaway" variable _. It's actually just like any other variable (with special usage in the console), except that some Python users decided to have it be "throwaway" as a convention.
count, _ = get_count_and_price()
About the PyDev problem, you should just use the [0] subscript anyway. But if you really want to use _ the only solution is to disable the unused variable warnings if that bothers you.
Using _ as severally proposed may have some issues (though it's mostly OK). By the Python style guidelines we use at work I'd normally use count, unused_price = ... since pylint is configured to ignore assignments to barenames starting with unused_ (and warn on USE of any such barename instead!-). But I don't know how to instruct PyDev to behave that way!
If you go to the Eclipse -> Preferences… window, you can actually specify which variable names PyDev should ignore if they're unused (I'm looking at the newest PyDev 1.5.X).
If you go to PyDev -> Editor -> Code Analysis and look at the last field that says "Don't report unused variable if name starts with"
Enter whatever names you want in there and then use that name to restrict what variable names PyDev will ignore unused warnings for.
By default, it looks like PyDev will hide unused variable warnings for any variables that have names beginning with "dummy", "_", or "unused".
As #TokenMacGuy said below, I'd recommend against using just "_" because it has special meaning in certain scenarios in Python (specifically it's used in the interactive interpreter).
We often do this.
count, _ = get_count_and_price()
or this
count, junk = get_count_and_price()
I'd rather name it _price instead, for these reasons:
It solves the conflict with gettext and the interactive prompt, which both use _
It's easy to change back into price if you end up needing it later.
As others have pointed out, the leading underscore already has a connotation of "internal" or "unused" in many languages.
So your code would end up looking like this:
(count, _price) = get_count_and_price()
I'll go after a Necromancer badge. :)
You said you're using PyDev. In PyDev (at least recent versions - I didn't check how far back), any variable name that starts with "unused" will be exempt from the Unused Variable warning. Other static analysis tool may still complain, though (pyflakes does - but it seems to ignore this warning in a tuple-unpacking context anyway).

Categories