-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
[WIP] introduce a distinct bytes type. #580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,8 @@ class int(SupportsInt, SupportsFloat, SupportsAbs[int]): | |
def __abs__(self) -> int: ... | ||
def __hash__(self) -> int: ... | ||
|
||
long = int | ||
|
||
class float(SupportsFloat, SupportsInt, SupportsAbs[float]): | ||
@overload | ||
def __init__(self) -> None: ... | ||
|
@@ -210,7 +212,7 @@ class unicode(basestring, Sequence[unicode]): | |
def center(self, width: int, fillchar: unicode = u' ') -> unicode: ... | ||
def count(self, x: unicode) -> int: ... | ||
def decode(self, encoding: unicode = ..., errors: unicode = ...) -> unicode: ... | ||
def encode(self, encoding: unicode = ..., errors: unicode = ...) -> str: ... | ||
def encode(self, encoding: unicode = ..., errors: unicode = ...) -> bytes: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. encode() is just about the strongest signal that we want bytes, apart from using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should things like
The idea of adding But having to write On the other hand, if you have your encoding name as a string parameter (as opposed to a literal, and maybe it's a more important case overall) and you make
So, a possible conclusion from this is: if you are sure that your argument gets implicitly ASCII-converted to a 8-bit string, then you should use |
||
def endswith(self, suffix: Union[unicode, Tuple[unicode, ...]], start: int = 0, | ||
end: int = ...) -> bool: ... | ||
def expandtabs(self, tabsize: int = 8) -> unicode: ... | ||
|
@@ -282,7 +284,7 @@ class str(basestring, Sequence[str]): | |
def center(self, width: int, fillchar: str = ...) -> str: ... | ||
def count(self, x: unicode) -> int: ... | ||
def decode(self, encoding: unicode = ..., errors: unicode = ...) -> unicode: ... | ||
def encode(self, encoding: unicode = ..., errors: unicode = ...) -> str: ... | ||
def encode(self, encoding: unicode = ..., errors: unicode = ...) -> bytes: ... | ||
def endswith(self, suffix: Union[unicode, Tuple[unicode, ...]]) -> bool: ... | ||
def expandtabs(self, tabsize: int = 8) -> str: ... | ||
def find(self, sub: unicode, start: int = 0, end: int = 0) -> int: ... | ||
|
@@ -366,11 +368,102 @@ class str(basestring, Sequence[str]): | |
def __ge__(self, x: unicode) -> bool: ... | ||
def __mod__(self, x: Any) -> str: ... | ||
|
||
class bytes(basestring, Sequence[bytes]): | ||
# TODO: double-check unicode, AnyStr, unions, bytearray | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So far this is just a clone of |
||
def __init__(self, object: object) -> None: ... | ||
def capitalize(self) -> bytes: ... | ||
def center(self, width: int, fillchar: bytes = ...) -> bytes: ... | ||
def count(self, x: unicode) -> int: ... | ||
def decode(self, encoding: unicode = ..., errors: unicode = ...) -> unicode: ... | ||
def encode(self, encoding: unicode = ..., errors: unicode = ...) -> bytes: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, bytes probably shouldn't have encode() at all. |
||
def endswith(self, suffix: Union[unicode, Tuple[unicode, ...]]) -> bool: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably use |
||
def expandtabs(self, tabsize: int = 8) -> bytes: ... | ||
def find(self, sub: unicode, start: int = 0, end: int = 0) -> int: ... | ||
def format(self, *args: Any, **kwargs: Any) -> bytes: ... | ||
def index(self, sub: unicode, start: int = 0, end: int = 0) -> int: ... | ||
def isalnum(self) -> bool: ... | ||
def isalpha(self) -> bool: ... | ||
def isdigit(self) -> bool: ... | ||
def islower(self) -> bool: ... | ||
def isspace(self) -> bool: ... | ||
def istitle(self) -> bool: ... | ||
def isupper(self) -> bool: ... | ||
def join(self, iterable: Iterable[AnyStr]) -> AnyStr: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably should just be bytes as well, no need for AnyStr here. bytes can only be joined with bytes. Right? |
||
def ljust(self, width: int, fillchar: bytes = ...) -> bytes: ... | ||
def lower(self) -> bytes: ... | ||
@overload | ||
def lstrip(self, chars: bytes = ...) -> bytes: ... | ||
@overload | ||
def lstrip(self, chars: unicode) -> unicode: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. |
||
@overload | ||
def partition(self, sep: bytearray) -> Tuple[bytes, bytearray, bytes]: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was an existing overload for bytearray in str as well. I think it's pretty unfortunate; we probably either will have to add a lot more of these, or get rid of them and do some form of auto-promotion of bytearray to bytes. |
||
@overload | ||
def partition(self, sep: bytes) -> Tuple[bytes, bytes, bytes]: ... | ||
@overload | ||
def partition(self, sep: unicode) -> Tuple[unicode, unicode, unicode]: ... | ||
def replace(self, old: AnyStr, new: AnyStr, count: int = ...) -> AnyStr: ... | ||
def rfind(self, sub: unicode, start: int = 0, end: int = 0) -> int: ... | ||
def rindex(self, sub: unicode, start: int = 0, end: int = 0) -> int: ... | ||
def rjust(self, width: int, fillchar: bytes = ...) -> bytes: ... | ||
@overload | ||
def rpartition(self, sep: bytearray) -> Tuple[bytes, bytearray, bytes]: ... | ||
@overload | ||
def rpartition(self, sep: bytes) -> Tuple[bytes, bytes, bytes]: ... | ||
@overload | ||
def rpartition(self, sep: unicode) -> Tuple[unicode, unicode, unicode]: ... | ||
@overload | ||
def rsplit(self, sep: Optional[bytes] = ..., maxsplit: int = ...) -> List[bytes]: ... | ||
@overload | ||
def rsplit(self, sep: unicode, maxsplit: int = ...) -> List[unicode]: ... | ||
@overload | ||
def rstrip(self, chars: bytes = ...) -> bytes: ... | ||
@overload | ||
def rstrip(self, chars: unicode) -> unicode: ... | ||
@overload | ||
def split(self, sep: Optional[bytes] = ..., maxsplit: int = ...) -> List[bytes]: ... | ||
@overload | ||
def split(self, sep: unicode, maxsplit: int = ...) -> List[unicode]: ... | ||
def splitlines(self, keepends: bool = ...) -> List[bytes]: ... | ||
def startswith(self, prefix: Union[unicode, Tuple[unicode, ...]]) -> bool: ... | ||
@overload | ||
def strip(self, chars: bytes = ...) -> bytes: ... | ||
@overload | ||
def strip(self, chars: unicode) -> unicode: ... | ||
def swapcase(self) -> bytes: ... | ||
def title(self) -> bytes: ... | ||
def translate(self, table: Optional[AnyStr], deletechars: AnyStr = ...) -> AnyStr: ... | ||
def upper(self) -> bytes: ... | ||
def zfill(self, width: int) -> bytes: ... | ||
|
||
def __len__(self) -> int: ... | ||
def __iter__(self) -> Iterator[bytes]: ... | ||
def __str__(self) -> str: ... | ||
def __repr__(self) -> str: ... | ||
def __int__(self) -> int: ... | ||
def __float__(self) -> float: ... | ||
def __hash__(self) -> int: ... | ||
@overload | ||
def __getitem__(self, i: int) -> bytes: ... | ||
@overload | ||
def __getitem__(self, s: slice) -> bytes: ... | ||
def __getslice__(self, start: int, stop: int) -> bytes: ... | ||
def __add__(self, s: AnyStr) -> AnyStr: ... | ||
def __mul__(self, n: int) -> bytes: ... | ||
def __rmul__(self, n: int) -> bytes: ... | ||
def __contains__(self, o: object) -> bool: ... | ||
def __eq__(self, x: object) -> bool: ... | ||
def __ne__(self, x: object) -> bool: ... | ||
def __lt__(self, x: unicode) -> bool: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely wrong. |
||
def __le__(self, x: unicode) -> bool: ... | ||
def __gt__(self, x: unicode) -> bool: ... | ||
def __ge__(self, x: unicode) -> bool: ... | ||
def __mod__(self, x: Any) -> bytes: ... | ||
|
||
class bytearray(MutableSequence[int]): | ||
@overload | ||
def __init__(self) -> None: ... | ||
@overload | ||
def __init__(self, x: Union[Iterable[int], str]) -> None: ... | ||
def __init__(self, x: Union[Iterable[int], bytes]) -> None: ... | ||
@overload | ||
def __init__(self, x: unicode, encoding: unicode, | ||
errors: unicode = ...) -> None: ... | ||
|
@@ -659,9 +752,6 @@ class property: | |
def __set__(self, obj: Any, value: Any) -> None: ... | ||
def __delete__(self, obj: Any) -> None: ... | ||
|
||
long = int | ||
bytes = str | ||
|
||
NotImplemented = ... # type: Any | ||
|
||
def abs(n: SupportsAbs[_T]) -> _T: ... | ||
|
@@ -716,11 +806,11 @@ def next(i: Iterator[_T]) -> _T: ... | |
def next(i: Iterator[_T], default: _T) -> _T: ... | ||
def oct(i: int) -> str: ... # TODO __index__ | ||
@overload | ||
def open(file: str, mode: str = 'r', buffering: int = ...) -> BinaryIO: ... | ||
def open(file: str, mode: str = 'r', buffering: int = ...) -> IO[str]: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a the first big dilemma. Python 2's open() always returns a stream that reads/writes str instances (never unicode) but for various reasons BinaryIO derives from IO[bytes], not from IO[str]. BinaryIO adds some extra methods that aren't defined on IO, so we should really have a subclass of IO[str] that adds those same methods. But what to call it? StringIO would be really confusing. There's of course also the issue that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't it return |
||
@overload | ||
def open(file: unicode, mode: str = 'r', buffering: int = ...) -> BinaryIO: ... | ||
def open(file: unicode, mode: str = 'r', buffering: int = ...) -> IO[str]: ... | ||
@overload | ||
def open(file: int, mode: str = 'r', buffering: int = ...) -> BinaryIO: ... | ||
def open(file: int, mode: str = 'r', buffering: int = ...) -> IO[str]: ... | ||
def ord(c: unicode) -> int: ... | ||
# This is only available after from __future__ import print_function. | ||
def print(*values: Any, sep: unicode = u' ', end: unicode = u'\n', | ||
|
@@ -913,8 +1003,8 @@ class file(BinaryIO): | |
def __init__(self, file: unicode, mode: str = 'r', buffering: int = ...) -> None: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, maybe we can commandeer 'file' (a PY2-only class) to be the subclass of IO[str] I wished for above. |
||
@overload | ||
def __init__(file: int, mode: str = 'r', buffering: int = ...) -> None: ... | ||
def __iter__(self) -> Iterator[str]: ... | ||
def read(self, n: int = ...) -> str: ... | ||
def __iter__(self) -> Iterator[bytes]: ... | ||
def read(self, n: int = ...) -> bytes: ... | ||
def __enter__(self) -> BinaryIO: ... | ||
def __exit__(self, t: type = None, exc: BaseException = None, tb: Any = None) -> bool: ... | ||
def flush(self) -> None: ... | ||
|
@@ -927,10 +1017,10 @@ class file(BinaryIO): | |
def seekable(self) -> bool: ... | ||
def seek(self, offset: int, whence: int = ...) -> None: ... | ||
def tell(self) -> int: ... | ||
def readline(self, limit: int = ...) -> str: ... | ||
def readlines(self, hint: int = ...) -> List[str]: ... | ||
def write(self, data: str) -> None: ... | ||
def writelines(self, data: Iterable[str]) -> None: ... | ||
def readline(self, limit: int = ...) -> bytes: ... | ||
def readlines(self, hint: int = ...) -> List[bytes]: ... | ||
def write(self, data: bytes) -> None: ... | ||
def writelines(self, data: Iterable[bytes]) -> None: ... | ||
def truncate(self, pos: int = ...) -> int: ... | ||
|
||
# Very old builtins | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,30 +18,30 @@ class IOBase: | |
... | ||
|
||
class BytesIO(BinaryIO): | ||
def __init__(self, initial_bytes: str = ...) -> None: ... | ||
def __init__(self, initial_bytes: bytes = ...) -> None: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everything in this file honestly looks like an improvement (I may even keep it regardless of what we do here, since bytes would just be an alias for str). |
||
# TODO getbuffer | ||
# TODO see comments in BinaryIO for missing functionality | ||
def close(self) -> None: ... | ||
def closed(self) -> bool: ... | ||
def fileno(self) -> int: ... | ||
def flush(self) -> None: ... | ||
def isatty(self) -> bool: ... | ||
def read(self, n: int = ...) -> str: ... | ||
def read(self, n: int = ...) -> bytes: ... | ||
def readable(self) -> bool: ... | ||
def readline(self, limit: int = ...) -> str: ... | ||
def readlines(self, hint: int = ...) -> List[str]: ... | ||
def readline(self, limit: int = ...) -> bytes: ... | ||
def readlines(self, hint: int = ...) -> List[bytes]: ... | ||
def seek(self, offset: int, whence: int = ...) -> None: ... | ||
def seekable(self) -> bool: ... | ||
def tell(self) -> int: ... | ||
def truncate(self, size: int = ...) -> int: ... | ||
def writable(self) -> bool: ... | ||
def write(self, s: str) -> None: ... | ||
def writelines(self, lines: Iterable[str]) -> None: ... | ||
def getvalue(self) -> str: ... | ||
def read1(self) -> str: ... | ||
def write(self, s: bytes) -> None: ... | ||
def writelines(self, lines: Iterable[bytes]) -> None: ... | ||
def getvalue(self) -> bytes: ... | ||
def read1(self) -> bytes: ... | ||
|
||
def __iter__(self) -> Iterator[str]: ... | ||
def next(self) -> str: ... | ||
def __iter__(self) -> Iterator[bytes]: ... | ||
def next(self) -> bytes: ... | ||
def __enter__(self) -> 'BytesIO': ... | ||
def __exit__(self, type, value, traceback) -> bool: ... | ||
|
||
|
@@ -74,7 +74,7 @@ class StringIO(TextIO): | |
|
||
class TextIOWrapper(TextIO): | ||
# write_through is undocumented but used by subprocess | ||
def __init__(self, buffer: IO[str], encoding: unicode = ..., | ||
def __init__(self, buffer: IO[bytes], encoding: unicode = ..., | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I've got a feeling that the buffer argument here is actually more restricted than IO[] -- it probably needs to be derived from BufferedIOBase. |
||
errors: unicode = ..., newline: unicode = ..., | ||
line_buffering: bool = ..., | ||
write_through: bool = ...) -> None: ... | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,63 +34,63 @@ def compile(pattern: AnyStr, flags: int = ...) -> Pattern[AnyStr]: ... | |
def compile(pattern: Pattern[AnyStr], flags: int = ...) -> Pattern[AnyStr]: ... | ||
|
||
@overload | ||
def search(pattern: Union[str, unicode], string: AnyStr, flags: int = ...) -> Match[AnyStr]: ... | ||
def search(pattern: Union[bytes, str, unicode], string: AnyStr, flags: int = ...) -> Match[AnyStr]: ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Every API in this file got amended to add bytes to the union or overload. Beware, it's the pattern that's got the unions -- the search target uses AnyStr and the return type varies with that. But (at least in PY2) the pattern can vary independently. |
||
@overload | ||
def search(pattern: Union[Pattern[str],Pattern[unicode]], string: AnyStr, flags: int = ...) -> Match[AnyStr]: ... | ||
def search(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], string: AnyStr, flags: int = ...) -> Match[AnyStr]: ... | ||
|
||
@overload | ||
def match(pattern: Union[str, unicode], string: AnyStr, flags: int = ...) -> Match[AnyStr]: ... | ||
def match(pattern: Union[bytes, str, unicode], string: AnyStr, flags: int = ...) -> Match[AnyStr]: ... | ||
@overload | ||
def match(pattern: Union[Pattern[str],Pattern[unicode]], string: AnyStr, flags: int = ...) -> Match[AnyStr]: ... | ||
def match(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], string: AnyStr, flags: int = ...) -> Match[AnyStr]: ... | ||
|
||
@overload | ||
def split(pattern: Union[str, unicode], string: AnyStr, | ||
def split(pattern: Union[bytes, str, unicode], string: AnyStr, | ||
maxsplit: int = ..., flags: int = ...) -> List[AnyStr]: ... | ||
@overload | ||
def split(pattern: Union[Pattern[str],Pattern[unicode]], string: AnyStr, | ||
def split(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], string: AnyStr, | ||
maxsplit: int = ..., flags: int = ...) -> List[AnyStr]: ... | ||
|
||
@overload | ||
def findall(pattern: Union[str, unicode], string: AnyStr, flags: int = ...) -> List[Any]: ... | ||
def findall(pattern: Union[bytes, str, unicode], string: AnyStr, flags: int = ...) -> List[Any]: ... | ||
@overload | ||
def findall(pattern: Union[Pattern[str],Pattern[unicode]], string: AnyStr, flags: int = ...) -> List[Any]: ... | ||
def findall(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], string: AnyStr, flags: int = ...) -> List[Any]: ... | ||
|
||
# Return an iterator yielding match objects over all non-overlapping matches | ||
# for the RE pattern in string. The string is scanned left-to-right, and | ||
# matches are returned in the order found. Empty matches are included in the | ||
# result unless they touch the beginning of another match. | ||
@overload | ||
def finditer(pattern: Union[str, unicode], string: AnyStr, | ||
def finditer(pattern: Union[bytes, str, unicode], string: AnyStr, | ||
flags: int = ...) -> Iterator[Match[AnyStr]]: ... | ||
@overload | ||
def finditer(pattern: Union[Pattern[str],Pattern[unicode]], string: AnyStr, | ||
def finditer(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], string: AnyStr, | ||
flags: int = ...) -> Iterator[Match[AnyStr]]: ... | ||
|
||
@overload | ||
def sub(pattern: Union[str, unicode], repl: AnyStr, string: AnyStr, count: int = ..., | ||
def sub(pattern: Union[bytes, str, unicode], repl: AnyStr, string: AnyStr, count: int = ..., | ||
flags: int = ...) -> AnyStr: ... | ||
@overload | ||
def sub(pattern: Union[str, unicode], repl: Callable[[Match[AnyStr]], AnyStr], | ||
def sub(pattern: Union[bytes, str, unicode], repl: Callable[[Match[AnyStr]], AnyStr], | ||
string: AnyStr, count: int = ..., flags: int = ...) -> AnyStr: ... | ||
@overload | ||
def sub(pattern: Union[Pattern[str],Pattern[unicode]], repl: AnyStr, string: AnyStr, count: int = ..., | ||
def sub(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], repl: AnyStr, string: AnyStr, count: int = ..., | ||
flags: int = ...) -> AnyStr: ... | ||
@overload | ||
def sub(pattern: Union[Pattern[str],Pattern[unicode]], repl: Callable[[Match[AnyStr]], AnyStr], | ||
def sub(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], repl: Callable[[Match[AnyStr]], AnyStr], | ||
string: AnyStr, count: int = ..., flags: int = ...) -> AnyStr: ... | ||
|
||
@overload | ||
def subn(pattern: Union[str, unicode], repl: AnyStr, string: AnyStr, count: int = ..., | ||
def subn(pattern: Union[bytes, str, unicode], repl: AnyStr, string: AnyStr, count: int = ..., | ||
flags: int = ...) -> Tuple[AnyStr, int]: ... | ||
@overload | ||
def subn(pattern: Union[str, unicode], repl: Callable[[Match[AnyStr]], AnyStr], | ||
def subn(pattern: Union[bytes, str, unicode], repl: Callable[[Match[AnyStr]], AnyStr], | ||
string: AnyStr, count: int = ..., | ||
flags: int = ...) -> Tuple[AnyStr, int]: ... | ||
@overload | ||
def subn(pattern: Union[Pattern[str],Pattern[unicode]], repl: AnyStr, string: AnyStr, count: int = ..., | ||
def subn(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], repl: AnyStr, string: AnyStr, count: int = ..., | ||
flags: int = ...) -> Tuple[AnyStr, int]: ... | ||
@overload | ||
def subn(pattern: Union[Pattern[str],Pattern[unicode]], repl: Callable[[Match[AnyStr]], AnyStr], | ||
def subn(pattern: Union[Pattern[bytes], Pattern[str], Pattern[unicode]], repl: Callable[[Match[AnyStr]], AnyStr], | ||
string: AnyStr, count: int = ..., | ||
flags: int = ...) -> Tuple[AnyStr, int]: ... | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ DefaultDict = TypeAlias(object) | |
Set = TypeAlias(object) | ||
|
||
# Predefined type variables. | ||
AnyStr = TypeVar('AnyStr', str, unicode) | ||
AnyStr = TypeVar('AnyStr', bytes, str, unicode) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is key, but also annoying. E.g. this code breaks because of this:
You have to add a third case ( |
||
|
||
# Abstract base classes. | ||
|
||
|
@@ -262,7 +262,7 @@ class IO(Iterator[AnyStr], Generic[AnyStr]): | |
# TODO: traceback should be TracebackType but that's defined in types | ||
traceback: Optional[Any]) -> bool: ... | ||
|
||
class BinaryIO(IO[str]): | ||
class BinaryIO(IO[bytes]): | ||
# TODO readinto | ||
# TODO read1? | ||
# TODO peek? | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just moved this to a more logical place.