Best practice for using assert?
Asked Answered
T

15

600
  1. Is there a performance or code maintenance issue with using assert as part of the standard code instead of using it just for debugging purposes?

    Is

    assert x >= 0, 'x is less than zero'
    

    better or worse than

    if x < 0:
        raise Exception('x is less than zero')
    
  2. Also, is there any way to set a business rule like if x < 0 raise error that is always checked without the try/except/finally so, if at anytime throughout the code x is less than 0 an error is raised, like if you set assert x < 0 at the start of a function, anywhere within the function where x becomes less then 0 an exception is raised?

Telefilm answered 3/6, 2009 at 12:57 Comment(5)
-O and -OO python parameters will strip away your assertions. That should drive your thinking on what it's good for.Herculean
Thomasz Zielinski's link got broken, it's now: mail.python.org/pipermail/python-list/2013-November/660568.html . I'm pretty sure pipermail has an unstable ID function, I found other links from inside the same pipermail pointing to the same url with the same intention.Henley
In case mail.python.org/pipermail/python-list/2013-November/660568.html moves again, it is archived at archive.is/5GfiG . The title of the post is "When to use assert" and is an excellent post (an article really) on best practices for Python assert.Spinal
Does this answer your question? What is the use of "assert" in Python?Customs
I never see people run python scripts with "-O" or "-OO" in practiceTransformation
P
165

To be able to automatically throw an error when x become less than zero throughout the function. You can use class descriptors. Here is an example:

class LessThanZeroException(Exception):
    pass

class variable(object):
    def __init__(self, value=0):
        self.__x = value

    def __set__(self, obj, value):
        if value < 0:
            raise LessThanZeroException('x is less than zero')

        self.__x  = value

    def __get__(self, obj, objType):
        return self.__x

class MyClass(object):
    x = variable()

>>> m = MyClass()
>>> m.x = 10
>>> m.x -= 20
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "my.py", line 7, in __set__
    raise LessThanZeroException('x is less than zero')
LessThanZeroException: x is less than zero
Peptidase answered 3/6, 2009 at 13:12 Comment(10)
Although properties are implemented as descriptors, I wouldn't call this an example of using them. This is more an example of properties in and of themselves: docs.python.org/library/functions.html#propertyJeramyjerba
The properties should be used within MyClass when setting x. This solution is too general.Adi
Pretty nice answer, like it, but has to do NOTHING with the question... Cannot we mark Deestan or John Mee's answer as the valid response?Botchy
This doesn't appear to answer the question's title. Also, this is a poor alternative to Python's class property feature.Vardar
@VajkHermecz: Actually, if you reread the question, this is two questions in one. People only looking at the title are only familiar with the first question, which this answer doesn't answer. This answer actually contains an answer to the second question.Sidonie
@ArtOfWarfare: You are right, but that's why the second part should be a separate question. It is hard to deal with multiquestions...Botchy
@VajkHermecz: Yeah, I just brought this up on meta: meta.stackoverflow.com/questions/288121/…Sidonie
Though it is better to use but, seems original question was lost in somewhere in answer.Eckard
Is not in relevance with the questionWithout
Can this custom variable definition be used without MyClass and the backing __x field?Finance
H
870

Asserts should be used to test conditions that should never happen. The purpose is to crash early in the case of a corrupt program state.

Exceptions should be used for errors that can conceivably happen, and you should almost always create your own Exception classes.


For example, if you're writing a function to read from a configuration file into a dict, improper formatting in the file should raise a ConfigurationSyntaxError, while you can assert that you're not about to return None.


In your example, if x is a value set via a user interface or from an external source, an exception is best.

If x is only set by your own code in the same program, go with an assertion.

Herbie answered 3/6, 2009 at 14:34 Comment(11)
+1 for the last paragraph - though you should explicitly mention that assert contains an implicit if __debug__ and may be optimized away - as John Mee's answer statesFarriery
When is a certain condition unlikely enough to use an assert for? This is not a rule that can be strictly applied. You would need to define all conditions which are considered unlikely enough for this rule to be usable.Devest
Rereading your answer I think you probably didn't mean conditions that should never happen to be meant as a rule, but rather the purpose is to crash early in the case of a corrupt program state which usually coincides with a condition you don't expect to ever happen.Devest
I think the sole reason for using an assert instead of an Exception with an if is because the Exception message would be You can see this message because anything but the specified condition ran. This message is not specific/helpful enough that you'd be able to solve the problem without looking at the code. So it wouldn't matter if you wrote an assert or an Exception with an if because you'd have to take a look at the code anyway. And since writing an assert requires less typing than writing an exception, an assert is thus preferable.Devest
assert should only be used to catch problems with no known recovery; almost always code bugs (not bad inputs). when an assert is triggered, it should mean that the program is in a state that may be dangerous to continue in, as it may start talking to the network or writing to disk. robust code moves 'atomically' from valid state to valid state in the face of bad (or malicious) input. the top level of every thread should have a fault barrier. fault barriers that consume input from the outside world generally fail for just one iteration of the barrier (while/try), rollback/log on error.Evy
I understand it's purpose (now, thank you); but it still seems kind of a waste though. Such a quick-and-easy way to type if condition is False: Raise Error.Joris
"Asserts should be used to test conditions that should never happen." Yes. And the meaning of the second "should" is: If this happens, the program code is incorrect.Nabala
Ordinary exceptions signal non-ordinary (but acceptable) events in the program run. AssertionErrors signal defects in the program logic.Nabala
This is the best answer for me, but I miss something. If an assert will be removed if you byte-compile using -O, assert should not crash the program IMHO. The program should crash by itself, assert or not. assert should be used only as an informative statement, as @LutzPrechelt said in its answer.Semiliquid
IMO you should you should almost NEVER create your own Exception classes. You only care about the type of the exception if your code is going to act differently based on what went wrong; that's not very common. 90% of the time, your code only cares that something went wrong. The human who's debugging the problem cares what went wrong, which is what the exception's message is for.Clobber
With awareness that this answer was originally posted 12 years ago and things may be different now, I would agree with Alex that you should never write your own exceptions unless you really need to match several different kinds of fail-states and the standard types are colliding, forcing you to do string matching on the error message. If your code can fail that many ways in one place, you should probably rewrite your code.Wahkuna
T
459

"assert" statements are removed when the compilation is optimized. So, yes, there are both performance and functional differences.

The current code generator emits no code for an assert statement when optimization is requested at compile time. - Python 2 Docs Python 3 Docs

If you use assert to implement application functionality, then optimize the deployment to production, you will be plagued by "but-it-works-in-dev" defects.

See PYTHONOPTIMIZE and -O -OO

Tomi answered 3/12, 2009 at 8:15 Comment(3)
Wow! Super important note that is! I had been planning on using asserts to check a few things which should never fail, whose failure would indicate that someone was very carefully manipulating my the data they were sending in an attempt to gain access to data they shouldn't have access to. It wouldn't work, but I want to swiftly shut down their attempt with an assert, so having that optimized away in production would defeat the purpose. I guess I'll just raise an Exception instead. Oh - I just discovered an aptly named SuspiciousOperation Exception with subclasses in Django! Perfect!Sidonie
By the way @Sidonie if you run bandit on your code, it will warn you of this.Mccarver
@John Mee, thanks for important information. I used assert with oython version check for correct run on required version. but assert does not work for version check in the executble python script through #!/bin/python. Now I figure out the reason from your information on assert. Thank you.Routine
P
165

To be able to automatically throw an error when x become less than zero throughout the function. You can use class descriptors. Here is an example:

class LessThanZeroException(Exception):
    pass

class variable(object):
    def __init__(self, value=0):
        self.__x = value

    def __set__(self, obj, value):
        if value < 0:
            raise LessThanZeroException('x is less than zero')

        self.__x  = value

    def __get__(self, obj, objType):
        return self.__x

class MyClass(object):
    x = variable()

>>> m = MyClass()
>>> m.x = 10
>>> m.x -= 20
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "my.py", line 7, in __set__
    raise LessThanZeroException('x is less than zero')
LessThanZeroException: x is less than zero
Peptidase answered 3/6, 2009 at 13:12 Comment(10)
Although properties are implemented as descriptors, I wouldn't call this an example of using them. This is more an example of properties in and of themselves: docs.python.org/library/functions.html#propertyJeramyjerba
The properties should be used within MyClass when setting x. This solution is too general.Adi
Pretty nice answer, like it, but has to do NOTHING with the question... Cannot we mark Deestan or John Mee's answer as the valid response?Botchy
This doesn't appear to answer the question's title. Also, this is a poor alternative to Python's class property feature.Vardar
@VajkHermecz: Actually, if you reread the question, this is two questions in one. People only looking at the title are only familiar with the first question, which this answer doesn't answer. This answer actually contains an answer to the second question.Sidonie
@ArtOfWarfare: You are right, but that's why the second part should be a separate question. It is hard to deal with multiquestions...Botchy
@VajkHermecz: Yeah, I just brought this up on meta: meta.stackoverflow.com/questions/288121/…Sidonie
Though it is better to use but, seems original question was lost in somewhere in answer.Eckard
Is not in relevance with the questionWithout
Can this custom variable definition be used without MyClass and the backing __x field?Finance
N
161

The four purposes of assert

Assume you work on 200,000 lines of code with four colleagues Alice, Bernd, Carl, and Daphne. They call your code, you call their code.

Then assert has four roles:

  1. Inform Alice, Bernd, Carl, and Daphne what your code expects.
    Assume you have a method that processes a list of tuples and the program logic can break if those tuples are not immutable:

    def mymethod(listOfTuples):
        assert(all(type(tp)==tuple for tp in listOfTuples))
    

    This is more trustworthy than equivalent information in the documentation and much easier to maintain.

  2. Inform the computer what your code expects.
    assert enforces proper behavior from the callers of your code. If your code calls Alices's and Bernd's code calls yours, then without the assert, if the program crashes in Alices code, Bernd might assume it was Alice's fault, Alice investigates and might assume it was your fault, you investigate and tell Bernd it was in fact his. Lots of work lost.
    With asserts, whoever gets a call wrong, they will quickly be able to see it was their fault, not yours. Alice, Bernd, and you all benefit. Saves immense amounts of time.

  3. Inform the readers of your code (including yourself) what your code has achieved at some point.
    Assume you have a list of entries and each of them can be clean (which is good) or it can be smorsh, trale, gullup, or twinkled (which are all not acceptable). If it's smorsh it must be unsmorshed; if it's trale it must be baludoed; if it's gullup it must be trotted (and then possibly paced, too); if it's twinkled it must be twinkled again except on Thursdays. You get the idea: It's complicated stuff. But the end result is (or ought to be) that all entries are clean. The Right Thing(TM) to do is to summarize the effect of your cleaning loop as

    assert(all(entry.isClean() for entry in mylist))
    

    This statements saves a headache for everybody trying to understand what exactly it is that the wonderful loop is achieving. And the most frequent of these people will likely be yourself.

  4. Inform the computer what your code has achieved at some point.
    Should you ever forget to pace an entry needing it after trotting, the assert will save your day and avoid that your code breaks dear Daphne's much later.

In my mind, assert's two purposes of documentation (1 and 3) and safeguard (2 and 4) are equally valuable.
Informing the people may even be more valuable than informing the computer because it can prevent the very mistakes the assert aims to catch (in case 1) and plenty of subsequent mistakes in any case.

Nabala answered 24/9, 2013 at 11:33 Comment(7)
5. assert isinstance() help PyCharm (python IDE) to know type of variable, it is used for autocomplete.Uchida
Asserts self-document code assumptions for what is true at the current execution time. It's an assumption comment, which gets checked.Chastity
Regarding 2 and 4: You should be very careful that your asserts are not too strict. Else the asserts themselves may be the only thing keeping your program to be used in a more general setting. Especially asserting types goes against python's duck-typing.See
@Uchida Be careful about an overuse of isinstance() as described in this blog entry: "isinstance() considered harmful". You can now use docstrings to specify types in Pycharm.Dawes
Using asserts in one way of ensuring contract. More info about Design by Contract en.wikipedia.org/wiki/Design_by_contractUnmask
Type annotations (or sometimes typing.cast) are a more modern alternative to assert isinstance.Instinctive
Yes! Type hints and IDEs that use that type hints to check the code can replace a lot of asserts, but you have to make sure that others are using type checking, too. Python Type Hints: docs.python.org/3/library/typing.htmlFinance
H
24

In addition to the other answers, asserts themselves throw exceptions, but only AssertionErrors. From a utilitarian standpoint, assertions aren't suitable for when you need fine grain control over which exceptions you catch.

Hob answered 3/12, 2009 at 11:37 Comment(3)
Right. It would seem silly to catch assertion error exceptions in the caller.Cadelle
Very good point. A nuance that can be easily overlooked when just looking at the original questions from a macro level. Even if it weren't for the issue with assertions being dropped when optimizing, losing the specific details of what kind of error occurred would make debugging much more challenging. Cheers, outis!Devisee
Your answer can be read as if you may want to catch AssertionErrors, when you're OK with it being coarse-grained. In reality, you shouldn't be catching them.Lucillalucille
J
19

The only thing that's really wrong with this approach is that it's hard to make a very descriptive exception using assert statements. If you're looking for the simpler syntax, remember you can also do something like this:

class XLessThanZeroException(Exception):
    pass

def CheckX(x):
    if x < 0:
        raise XLessThanZeroException()

def foo(x):
    CheckX(x)
    #do stuff here

Another problem is that using assert for normal condition-checking is that it makes it difficult to disable the debugging asserts using the -O flag.

Jeramyjerba answered 3/6, 2009 at 13:12 Comment(1)
You can append an error message to an assertion. It's the second parameter. That will make it descriptive.Cadelle
T
14

The English language word assert here is used in the sense of swear, affirm, avow. It doesn't mean "check" or "should be". It means that you as a coder are making a sworn statement here:

# I solemnly swear that here I will tell the truth, the whole truth, 
# and nothing but the truth, under pains and penalties of perjury, so help me FSM
assert answer == 42

If the code is correct, barring Single-event upsets, hardware failures and such, no assert will ever fail. That is why the behaviour of the program to an end user must not be affected. Especially, an assert cannot fail even under exceptional programmatic conditions. It just doesn't ever happen. If it happens, the programmer should be zapped for it.

Thenceforth answered 1/9, 2017 at 6:9 Comment(0)
D
9

As has been said previously, assertions should be used when your code SHOULD NOT ever reach a point, meaning there is a bug there. Probably the most useful reason I can see to use an assertion is an invariant/pre/postcondition. These are something that must be true at the start or end of each iteration of a loop or a function.

For example, a recursive function (2 seperate functions so 1 handles bad input and the other handles bad code, cause it's hard to distinguish with recursion). This would make it obvious if I forgot to write the if statement, what had gone wrong.

def SumToN(n):
    if n <= 0:
        raise ValueError, "N must be greater than or equal to 0"
    else:
        return RecursiveSum(n)

def RecursiveSum(n):
    #precondition: n >= 0
    assert(n >= 0)
    if n == 0:
        return 0
    return RecursiveSum(n - 1) + n
    #postcondition: returned sum of 1 to n

These loop invariants often can be represented with an assertion.

Duodenal answered 6/3, 2013 at 2:22 Comment(7)
This is best done with decorators (@precondition and @postcondition )World
@World what is the concrete benefit of that?Cuttler
@ChieltenBrinke self documenting code, instead of #precondition: n >= 0 and an assert, he can just write @precondition(lambda n: n >= 0)World
@World Are those builtin decorators then? And how does one generate documention from that?Cuttler
@ChieltenBrinke not built-in but easy to implement stackoverflow.com/questions/12151182/… . For documentation just patch the __doc__ attribute by giving an additional stringWorld
A better solution than patching the docs would be to use functools.wraps, which does that kind of thing for you.Duodenal
There are pre- and post-condition decorators in the PythonDecoratorLibrary.Ogee
S
7

For what it's worth, if you're dealing with code which relies on assert to function properly, then adding the following code will ensure that asserts are enabled:

try:
    assert False
    raise Exception('Python assertions are not working. This tool relies on Python assertions to do its job. Possible causes are running with the "-O" flag or running a precompiled (".pyo" or ".pyc") module.')
except AssertionError:
    pass
Spica answered 23/3, 2018 at 13:56 Comment(1)
This doesn't answer OP's question which is about best practices.Curtate
L
6

Well, this is an open question, and I have two aspects that I want to touch on: when to add assertions and how to write the error messages.

Purpose

To explain it to a beginner - assertions are statements which can raise errors, but you won't be catching them. And they normally should not be raised, but in real life they sometimes do get raised anyway. And this is a serious situation, which the code cannot recover from, what we call a 'fatal error'.

Next, it's for 'debugging purposes', which, while correct, sounds very dismissive. I like the 'declaring invariants, which should never be violated' formulation better, although it works differently on different beginners... Some 'just get it', and others either don't find any use for it, or replace normal exceptions, or even control flow with it.

Style

In Python, assert is a statement, not a function! (remember assert(False, 'is true') will not raise. But, having that out of the way:

When, and how, to write the optional 'error message'?

This acually applies to unit testing frameworks, which often have many dedicated methods to do assertions (assertTrue(condition), assertFalse(condition), assertEqual(actual, expected) etc.). They often also provide a way to comment on the assertion.

In throw-away code you could do without the error messages.

In some cases, there is nothing to add to the assertion:

def dump(something): assert isinstance(something, Dumpable) # ...

But apart from that, a message is useful for communication with other programmers (which are sometimes interactive users of your code, e.g. in Ipython/Jupyter etc.).

Give them information, not just leak internal implementation details.

instead of:

assert meaningless_identifier <= MAGIC_NUMBER_XXX, 'meaningless_identifier is greater than MAGIC_NUMBER_XXX!!!'

write:

assert meaningless_identifier > MAGIC_NUMBER_XXX, 'reactor temperature above critical threshold'

or maybe even:

assert meaningless_identifier > MAGIC_NUMBER_XXX, f'reactor temperature({meaningless_identifier }) above critical threshold ({MAGIC_NUMBER_XXX})'

I know, I know - this is not a case for a static assertion, but I want to point to the informational value of the message.

Negative or positive message?

This may be conroversial, but it hurts me to read things like:

assert a == b, 'a is not equal to b'
  • these are two contradictory things written next to eachother. So whenever I have an influence on the codebase, I push for specifying what we want, by using extra verbs like 'must' and 'should', and not to say what we don't want.

    assert a == b, 'a must be equal to b'

Then, getting AssertionError: a must be equal to b is also readable, and the statement looks logical in code. Also, you can get something out of it without reading the traceback (which can sometimes not even be available).

Lucillalucille answered 15/12, 2019 at 3:10 Comment(0)
N
5

Is there a performance issue?

  • Please remember to "make it work first before you make it work fast".
    Very few percent of any program are usually relevant for its speed. You can always kick out or simplify an assert if it ever proves to be a performance problem -- and most of them never will.

  • Be pragmatic:
    Assume you have a method that processes a non-empty list of tuples and the program logic will break if those tuples are not immutable. You should write:

    def mymethod(listOfTuples):
        assert(all(type(tp)==tuple for tp in listOfTuples))
    

    This is probably fine if your lists tend to be ten entries long, but it can become a problem if they have a million entries. But rather than discarding this valuable check entirely you could simply downgrade it to

    def mymethod(listOfTuples):
        assert(type(listOfTuples[0])==tuple)  # in fact _all_ must be tuples!
    

    which is cheap but will likely catch most of the actual program errors anyway.

Nabala answered 24/9, 2013 at 11:45 Comment(5)
Should be assert(len(listOfTuples)==0 or type(listOfTyples[0])==tuple).Refined
No, it should not. That would be a much weaker test, because it no longer checks the 'non-empty' property, which the second assert checks. (The first does not, although it should.)Nabala
The second assert does not explicitly check the non-empty property; it's more of a side effect. If it were to raise an exception due to the list being empty, the person working with the code (somebody else or the author, a year after writing it) would stare at it, trying to figure out if the assert was really meant to catch the empty list situation, or if that's an error in the assert itself. Furthermore, I don't see how not checking for the empty case is "much weaker", whereas only checking the first element is "97% correct".Refined
@SergeyOrshanskiy It is much weaker because the list not being empty is also a precondition and your check will not detect if that precondition is violated. (I agree that a failure of assert(type(listOfTuples[0])==tuple) might be confusing in that case.)Nabala
Making your asserts faster is not really useful since in production code, (with python -O ), they will not run at allPanegyric
B
3

An Assert is to check -
1. the valid condition,
2. the valid statement,
3. true logic;
of source code. Instead of failing the whole project it gives an alarm that something is not appropriate in your source file.

In example 1, since variable 'str' is not null. So no any assert or exception get raised.

Example 1:

#!/usr/bin/python

str = 'hello Python!'
strNull = 'string is Null'

if __debug__:
    if not str: raise AssertionError(strNull)
print str

if __debug__:
    print 'FileName '.ljust(30,'.'),(__name__)
    print 'FilePath '.ljust(30,'.'),(__file__)


------------------------------------------------------

Output:
hello Python!
FileName ..................... hello
FilePath ..................... C:/Python\hello.py

In example 2, var 'str' is null. So we are saving the user from going ahead of faulty program by assert statement.

Example 2:

#!/usr/bin/python

str = ''
strNull = 'NULL String'

if __debug__:
    if not str: raise AssertionError(strNull)
print str

if __debug__:
    print 'FileName '.ljust(30,'.'),(__name__)
    print 'FilePath '.ljust(30,'.'),(__file__)


------------------------------------------------------

Output:
AssertionError: NULL String

The moment we don't want debug and realized the assertion issue in the source code. Disable the optimization flag

python -O assertStatement.py
nothing will get print

Broucek answered 17/10, 2016 at 11:4 Comment(0)
R
2

Both the use of assert and the raising of exceptions are about communication.

  • Assertions are statements about the correctness of code addressed at developers: An assertion in the code informs readers of the code about conditions that have to be fulfilled for the code being correct. An assertion that fails at run-time informs developers that there is a defect in the code that needs fixing.

  • Exceptions are indications about non-typical situations that can occur at run-time but can not be resolved by the code at hand, addressed at the calling code to be handled there. The occurence of an exception does not indicate that there is a bug in the code.

Best practice

Therefore, if you consider the occurence of a specific situation at run-time as a bug that you would like to inform the developers about ("Hi developer, this condition indicates that there is a bug somewhere, please fix the code.") then go for an assertion. If the assertion checks input arguments of your code, you should typically add to the documentation that your code has "undefined behaviour" when the input arguments violate that conditions.

If instead the occurrence of that very situation is not an indication of a bug in your eyes, but instead a (maybe rare but) possible situation that you think should rather be handled by the client code, raise an exception. The situations when which exception is raised should be part of the documentation of the respective code.

Is there a performance [...] issue with using assert

The evaluation of assertions takes some time. They can be eliminated at compile time, though. This has some consequences, however, see below.

Is there a [...] code maintenance issue with using assert

Normally assertions improve the maintainability of the code, since they improve readability by making assumptions explicit and during run-time regularly verifying these assumptions. This will also help catching regressions. There is one issue, however, that needs to be kept in mind: Expressions used in assertions should have no side-effects. As mentioned above, assertions can be eliminated at compile time - which means that also the potential side-effects would disappear. This can - unintendedly - change the behaviour of the code.

Resistance answered 6/2, 2020 at 20:3 Comment(0)
U
0

In IDE's such as PTVS, PyCharm, Wing assert isinstance() statements can be used to enable code completion for some unclear objects.

Uneasy answered 8/4, 2015 at 17:40 Comment(2)
This seems to predate the use of type annotations or of typing.cast.Instinctive
cf comments on Lutz Prechelt's answer (this is often not recommended, since you have better ways to specify types such as type hints)Panegyric
P
0

I'd add I often use assert to specify properties such as loop invariants or logical properties my code should have, much like I'd specify them in formally-verified software.

They serve both the purpose of informing readers, helping me reason, and checking I am not making a mistake in my reasoning. For example :

k = 0
for i in range(n):
    assert k == i * (i + 1) // 2
    k += i 
    #do some things      

or in more complicated situations:

def sorted(l):
   return all(l1 <= l2 for l1, l2 in zip(l, l[1:]))
 
def mergesort(l):
   if len(l) < 2: #python 3.10 will have match - case for this instead of checking length
      return l
   k = len(l // 2)
   l1 = mergesort(l[:k])
   l2 = mergesort(l[k:])
   assert sorted(l1) # here the asserts allow me to explicit what properties my code should have
   assert sorted(l2) # I expect them to be disabled in a production build
   return merge(l1, l2)

Since asserts are disabled when python is run in optimized mode, do not hesitate to write costly conditions in them, especially if it makes your code clearer and less bug-prone

Panegyric answered 12/5, 2021 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.