1

In my Python 2.7 program (sorry, we have a third-party precompiled Python module that's stuck at 2.7, and yes we're leaning on them to upgrade and yes they're planning on getting there), rather than having functions just return True or False:

foo = self.first_thing()
if not foo:
   return False
bar = self.second_thing()
if not bar:
   return False
else:
   return True

I wanted to let the caller be able to log information with details about the failure. So I created a 'Result' class:

class Result(object):
   def __init__(self, success, details):
      self.success = success
      self.details = details

   def __nonzero__(self):
      return self.success

And now I can return information to be logged:

foo = self.first_thing()
if not foo:
   return Result(False, "first_thing failed")
bar = self.second_thing()
if not bar:
   return Result(False, "second_thing failed")
else:
   return Result(True, "")

And that's all fine and good.

But now I'm noticing that every function constructs at least one of those happy Result values. So I wanted to be efficient and only create one that can be used all over the place:

class Result(object):
   def __init__(self, success, details):
      self.success = success
      self.details = details

Result.Success = Result(True, "")

And that's nice and works as expected:

foo = self.first_thing()
if not foo:
   return Result(False, "first_thing failed")
bar = self.second_thing()
if not bar:
   return Result(False, "second_thing failed")
else:
   return Result.Success

But PyCharm is not amused. Every place I have this, PyCharm gives me the warning "Unresolved attribute reference 'Success' for class 'Result'".

Is there a way to do this that keeps PyCharm happy?

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Betty Crokker
  • 3,001
  • 6
  • 34
  • 68
  • Yes, make `Success` a global variable (outside of the class). It doesn't make a lot of sense to have a class variable which is an object of the class itself – Tomerikoo May 22 '21 at 18:27

2 Answers2

2

You may want to make a separate predefined subclass of Result for a default 'Success' result.

(I misinterpreted your question at first; in case one finds this question and is just looking for a way to disable PyCharm inspections locally or globally; scroll down).

E.g:

class Result(object):
    def __init__(self, success: bool,
                       details: str):

      self.success = success
      self.details = details


class ResultSuccess(Result):

    def __init__(self, success=True,
                       details=''):

        super(Result, self).__init__(success,
                                     details)


foo = some_class.first_thing()

if not foo:
   return Result(False, "first_thing failed")
bar = some_class.second_thing()

if bar:
   return ResultSuccess()
else:
   return Result(False, "second_thing failed")

Note that for this to work in Python 2.7, you need to have the baseclass explicitly inheriting from object, to enable new-style class functionality and allow usage of super().

In Python 3, this is not needed, and super(Result, self).__init__(success=True, details='') can be replaced by just super().__init__(success=True, details='')

Preventing instantiation overhead

At risk of addressing too broad a scope, the potential issue of instantiating a new object every time is discussed shortly, as a reply to OP's comment.

Before you go this route, consider if it matters that the ResultSuccess object is instantiated on every use. Unless you instantiate it thousands to hundreds of thousands of times in quick succession, I think it is unlikely to meaningfully impact performance for most use cases (haven't tested it though). Besides, the object instances will be garbage collected if no longer used/referenced and shouldn't use memory after the scope where they are used is left.

If you want to do it anyway, you could use either a separate file i.e. module where you initialize a ResultSuccess; this also means you won't have to subclass:

# static_succesresult.py
class Result(object):

   def __init__(self, success: bool,
                      details: str):

      self.success = success
      self.details = details


ResultSuccess = Result(True, details='')

# elsewhere.py
from static_successresult import ResultSuccess

#logic which uses ResultSuccess

This should make it global. Alternatively you could make ResultSucces a singleton in various ways (I like the metaclass way of doing it if a module wouldn't suffice). Note however that this pattern is controversial. In my opinion it should be used with care, for reasons which are out of scope here. I think it's fine for a small class like this which shouldn't change state, but there are likely better ways to approach this. (You could also use the built-in Enum for example, although it also makes use of singletons.).

   

Disabling PyCharm inspections

You could disable particular inspections globally in the settings. Often one doesn't want to do this globally however.

This can also be done locally by placing:

# noinspection {particular_inspection_type}

... above a function or statement for which you would like to disable it. In your case, this would be:

# noinspection PyUnresolvedReferences
def function_example():
    return seemingly_unresolved_variable

or

# noinspection PyUnresolvedReferences
statement_example = seemingly_unresolved_variable

Fortunately, PyCharm can do this for you automatically, so you don't have to remember the names for all the inspection types.

Disable inspection highlighting on a per-file basis:

It is possible to disable the highlighting for inspections on a per-file basis as well, although not for a particular type of inspection; 'syntax', 'All problems', and 'None' are the available options. You can click on the warning symbol in the top-hand corner and select the desired option from the drop-down menu:

context menu in the right-hand corner allows one to choose degree of problem highlighting for the current file

Disabling inspections per statement or function:

(1) PyCharm underlines the code at issue having an unresolved reference: PyCharm underlines the code at issue having an unresolved reference

(2) Open the context menu (default=alt+enter): Open the context menu (default=alt+enter)

(3) Select disable inspection (for the desired context) in second context menu. In this case only 'suppress for statement' can be chosen as we are not in the context of a function:

Select disable inspection (for the desired context) in second context menu

(4) A line is added which will have PyCharm ignore the inspection for the selected scope:

A line is added which will have PyCharm ignore the inspection for the selected scope.

For more info, see 'Disabling and enabling inspections' on Jetbrain's website.

Below follows a textual description for suppressing warnings for functions/statements in case one is prevented from seeing the images:

When you bring up the context menu for an underlined statement (should be alt+enter by default, having the cursor placed at the code at issue), a suggestion to correct the problem pops up. If you pick this, a new dropdown menu appears which asks you to correct the problem. If you then use the right arrow or click on the right arrow for the suggested correction, a new dropdown menu appears with options like "suppress for statement" or "suppress for function".

Choose the one applicable to your case to automatically add a line indicating that the linter should ignore a particular inspection for the selected scope.

In your case, this would be:

# noinspection PyUnresolvedReferences.

jrbergen
  • 660
  • 5
  • 16
  • 1
    Creating the ResultSuccess class worked perfectly, thanks! I had to make two tweaks to your code: (1) sucess mis-spelled in super __init__, and (2) Python didn't like the whole super thing and I reverted to the old fashioned Result.__init__(self, ...) – Betty Crokker May 24 '21 at 13:48
  • Although I just noticed ... I'm still creating a brand new ResultSuccess() object every time a function wants to return success; I had hoped to avoid that by having some kind of static/global variable. – Betty Crokker May 24 '21 at 14:00
  • @BettyCrokker Thanks, I'll correct it! I'll also update it to address the static/global consideration. – jrbergen May 24 '21 at 14:32
  • @BettyCrokker 'Python didn't like the whole super thing'. This is probably because in my example the function signature of the `ResultSuccess`'s __init__ differs from its parent. It should work if I correct that. One of the benefits of super is that if you ever wanted to use a different baseclass, you could switch it without having to change `ResultSuccess` as long as it has the same implementation, and it would automagically work. It also takes care of calling subsequent constructors implementing super() by itself (but be careful with the MRO. – jrbergen May 24 '21 at 14:55
  • @BettyCrokker See also: [Python Super considered harmful](https://fuhm.net/super-harmful/) and [Python Super considered super](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/). – jrbergen May 24 '21 at 15:01
-1

I haven't used PyCharm in a while, but I think adding a type annotation might work:

from typing import ClassVar

class Result(object):
   Success: ClassVar['Result']
   def __init__(self, success, details):
      self.success = success
      self.details = details

Result.Success = Result(True, "")
Jasmijn
  • 9,370
  • 2
  • 29
  • 43