Repository: praw-dev/praw

Issue:
Name shadowing due to permalink becoming a Reddit-provided JSON attribute. 
See https://www.reddit.com/r/redditdev/comments/78bgvn/praw_has_reddit_changed_the_api_in_such_a_way/.

[Reddit-provided JSON comment objects](https://gist.github.com/jarhill0/9aa047651d4289ff2ea5241465ebb967) now have a `permalink` attribute. This wasn't present previously, and now that it is, there ends up being a naming conflict with the `Comment.permalink()` method:

https://github.com/praw-dev/praw/blob/fb836c46a381a937df8659fb9e8ef959f7e734f9/praw/models/reddit/comment.py#L149-L168

Code such as the following worked in the recent past but will no longer work due to name shadowing:

```
my_comment = reddit.comment(id='dosiyca')
print(my_comment.permalink())  # raises TypeError: 'str' object is not callable
```

I believe there are two ways to resolve this issue. The first would be to remove the `Comment.permalink()` method and instead tell PRAW users to use the `Comment.permalink` attribute provided by Reddit. This change would be backwards-incompatible.

Another way to fix this would be to somehow fix the naming conflict (make `Comment.permalink` a method again, rather than a string attribute), and update `Comment.permalink()` to just return the `permalink` attribute Reddit provides. This fix would be backwards-compatible, as `Comment.permalink()` would still return exactly what PRAW users expect.

## System Information

PRAW Version: 5.1.1
Python Version: any
Operating System: any

Name shadowing due to permalink becoming a Reddit-provided JSON attribute. 
See https://www.reddit.com/r/redditdev/comments/78bgvn/praw_has_reddit_changed_the_api_in_such_a_way/.

[Reddit-provided JSON comment objects](https://gist.github.com/jarhill0/9aa047651d4289ff2ea5241465ebb967) now have a `permalink` attribute. This wasn't present previously, and now that it is, there ends up being a naming conflict with the `Comment.permalink()` method:

https://github.com/praw-dev/praw/blob/fb836c46a381a937df8659fb9e8ef959f7e734f9/praw/models/reddit/comment.py#L149-L168

Code such as the following worked in the recent past but will no longer work due to name shadowing:

```
my_comment = reddit.comment(id='dosiyca')
print(my_comment.permalink())  # raises TypeError: 'str' object is not callable
```

I believe there are two ways to resolve this issue. The first would be to remove the `Comment.permalink()` method and instead tell PRAW users to use the `Comment.permalink` attribute provided by Reddit. This change would be backwards-incompatible.

Another way to fix this would be to somehow fix the naming conflict (make `Comment.permalink` a method again, rather than a string attribute), and update `Comment.permalink()` to just return the `permalink` attribute Reddit provides. This fix would be backwards-compatible, as `Comment.permalink()` would still return exactly what PRAW users expect.

## System Information

PRAW Version: 5.1.1
Python Version: any
Operating System: any



Discussion:
I'm messing around with this and found something that might be useful. 

```
>>> my_comment = reddit.comment(id='dosiyca')
>>> print(my_comment.permalink)
<bound method Comment.permalink of Comment(id='dosiyca')>
>>> print(my_comment.body)
Test

Edit: Yes, it would appear permalink is now a first-class attribute on comments. Please use `comment.permalink` instead of `comment.permalink()`.
>>> print(my_comment.permalink)
/r/redditdev/comments/78bgvn/praw_has_reddit_changed_the_api_in_such_a_way/dosiyca/
```

As you can see, referencing `my_comment.permalink` before the comment has been fully loaded returns the `permalink()` method. But once the comment has loaded from the Reddit API, the API's `permalink` key overrides the method.

In fact, when a comment has not been fully loaded, calling its `Comment.permalink()` method functions as intended.
This is problematic on older versions of PRAW as well (3.6.1 in my case). Any chance of having a fix back ported to older major releases?
I suggest we simply remove the permalink method because it was merely a convenience while Reddit didn't have it.

@levi-rs perhaps you can look into something similar for PRAW 3.6.1. I'm happy to release a patch version that removes the corresponding permalink method, but I won't put any additional efforts into it.
@bboe Removing the permalink method would break a lot of scripts that expect it to exist, right? Wouldn't that require bumping the major version number?
@jarhill0 normally yes, but given that those uses are currently broken this can certainly fall into the bugfix category as well. If someone feels strongly about calling this a major version, I'm okay with that as well.

I commented to that effect here: https://github.com/praw-dev/praw/pull/857#issuecomment-339027782
I'm messing around with this and found something that might be useful. 

```
>>> my_comment = reddit.comment(id='dosiyca')
>>> print(my_comment.permalink)
<bound method Comment.permalink of Comment(id='dosiyca')>
>>> print(my_comment.body)
Test

Edit: Yes, it would appear permalink is now a first-class attribute on comments. Please use `comment.permalink` instead of `comment.permalink()`.
>>> print(my_comment.permalink)
/r/redditdev/comments/78bgvn/praw_has_reddit_changed_the_api_in_such_a_way/dosiyca/
```

As you can see, referencing `my_comment.permalink` before the comment has been fully loaded returns the `permalink()` method. But once the comment has loaded from the Reddit API, the API's `permalink` key overrides the method.

In fact, when a comment has not been fully loaded, calling its `Comment.permalink()` method functions as intended.
This is problematic on older versions of PRAW as well (3.6.1 in my case). Any chance of having a fix back ported to older major releases?
I suggest we simply remove the permalink method because it was merely a convenience while Reddit didn't have it.

@levi-rs perhaps you can look into something similar for PRAW 3.6.1. I'm happy to release a patch version that removes the corresponding permalink method, but I won't put any additional efforts into it.
@bboe Removing the permalink method would break a lot of scripts that expect it to exist, right? Wouldn't that require bumping the major version number?
@jarhill0 normally yes, but given that those uses are currently broken this can certainly fall into the bugfix category as well. If someone feels strongly about calling this a major version, I'm okay with that as well.

I commented to that effect here: https://github.com/praw-dev/praw/pull/857#issuecomment-339027782

Resolving patch:
diff --git a/praw/models/reddit/comment.py b/praw/models/reddit/comment.py
--- a/praw/models/reddit/comment.py
+++ b/praw/models/reddit/comment.py
@@ -1,5 +1,4 @@
 """Provide the Comment class."""
-from ...const import urljoin
 from ...exceptions import ClientException
 from ..comment_forest import CommentForest
 from .base import RedditBase
@@ -146,27 +145,6 @@ def parent(self):
         parent._submission = self.submission
         return parent
 
-    def permalink(self, fast=False):
-        """Return a permalink to the comment.
-
-        :param fast: Return the result as quickly as possible (default: False).
-
-        In order to determine the full permalink for a comment, the Submission
-        may need to be fetched if it hasn't been already. Set ``fast=True`` if
-        you want to bypass that possible load.
-
-        A full permalink looks like:
-        /r/redditdev/comments/2gmzqe/praw_https_enabled/cklhv0f
-
-        A fast-loaded permalink for the same comment will look like:
-        /comments/2gmzqe//cklhv0f
-
-        """
-        # pylint: disable=no-member
-        if not fast or 'permalink' in self.submission.__dict__:
-            return urljoin(self.submission.permalink, self.id)
-        return '/comments/{}//{}'.format(self.submission.id, self.id)
-
     def refresh(self):
         """Refresh the comment's attributes.
 
diff --git a/praw/reddit.py b/praw/reddit.py
--- a/praw/reddit.py
+++ b/praw/reddit.py
@@ -412,7 +412,7 @@ def generator():
                 url_list = [result for result in
                             self.get(API_PATH['info'], params=params)]
                 return url_list
-            except:
+            except Exception:
                 raise TypeError('Invalid URL or no posts exist')
 
     def post(self, path, data=None, files=None, params=None):


Test patch:
diff --git a/tests/integration/cassettes/TestComment.test_permalink.json b/tests/integration/cassettes/TestComment.test_permalink.json
deleted file mode 100644
--- a/tests/integration/cassettes/TestComment.test_permalink.json
+++ /dev/null
@@ -1,149 +0,0 @@
-{
-  "http_interactions": [
-    {
-      "recorded_at": "2016-03-21T07:39:20",
-      "request": {
-        "body": {
-          "encoding": "utf-8",
-          "string": "grant_type=client_credentials"
-        },
-        "headers": {
-          "Accept": "*/*",
-          "Accept-Encoding": "identity",
-          "Authorization": "Basic <BASIC_AUTH>",
-          "Connection": "keep-alive",
-          "Content-Length": "29",
-          "Content-Type": "application/x-www-form-urlencoded",
-          "User-Agent": "<USER_AGENT> PRAW/4.0.0a1 prawcore/0.0.7"
-        },
-        "method": "POST",
-        "uri": "https://www.reddit.com/api/v1/access_token"
-      },
-      "response": {
-        "body": {
-          "encoding": "UTF-8",
-          "string": "{\"access_token\": \"-XSZVbwSv9MstucyvALj72ltEJtc\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}"
-        },
-        "headers": {
-          "CF-RAY": "286fbfdb34913988-PHX",
-          "Connection": "keep-alive",
-          "Content-Length": "106",
-          "Content-Type": "application/json; charset=UTF-8",
-          "Date": "Mon, 21 Mar 2016 07:39:20 GMT",
-          "Server": "cloudflare-nginx",
-          "Set-Cookie": "__cfduid=db704537c2aa1a6a32f089173fc65db5c1458545960; expires=Tue, 21-Mar-17 07:39:20 GMT; path=/; domain=.reddit.com; HttpOnly",
-          "Strict-Transport-Security": "max-age=15552000; includeSubDomains; preload",
-          "X-Moose": "majestic",
-          "cache-control": "max-age=0, must-revalidate",
-          "x-content-type-options": "nosniff",
-          "x-frame-options": "SAMEORIGIN",
-          "x-xss-protection": "1; mode=block"
-        },
-        "status": {
-          "code": 200,
-          "message": "OK"
-        },
-        "url": "https://www.reddit.com/api/v1/access_token"
-      }
-    },
-    {
-      "recorded_at": "2016-03-21T07:39:20",
-      "request": {
-        "body": {
-          "encoding": "utf-8",
-          "string": ""
-        },
-        "headers": {
-          "Accept": "*/*",
-          "Accept-Encoding": "identity",
-          "Authorization": "bearer -XSZVbwSv9MstucyvALj72ltEJtc",
-          "Connection": "keep-alive",
-          "Cookie": "loid=3Rw5QbNUcPvfBaF0lr; __cfduid=db704537c2aa1a6a32f089173fc65db5c1458545960; loidcreated=2016-03-21T07%3A39%3A20.431Z",
-          "User-Agent": "<USER_AGENT> PRAW/4.0.0a1 prawcore/0.0.7"
-        },
-        "method": "GET",
-        "uri": "https://oauth.reddit.com/api/info/?id=t1_cklhv0f&raw_json=1"
-      },
-      "response": {
-        "body": {
-          "encoding": "UTF-8",
-          "string": "{\"kind\": \"Listing\", \"data\": {\"modhash\": \"\", \"children\": [{\"kind\": \"t1\", \"data\": {\"subreddit_id\": \"t5_2qizd\", \"banned_by\": null, \"removal_reason\": null, \"link_id\": \"t3_2gmzqe\", \"likes\": null, \"replies\": \"\", \"user_reports\": [], \"saved\": false, \"id\": \"cklhv0f\", \"gilded\": 0, \"archived\": true, \"report_reasons\": null, \"author\": \"bboe\", \"parent_id\": \"t1_cklfmye\", \"score\": 1, \"approved_by\": null, \"controversiality\": 0, \"body\": \"Yes it does. That fix is also in the master branch, we just haven't made a release for it (and we probably won't until 3.0).\", \"edited\": false, \"author_flair_css_class\": null, \"downs\": 0, \"body_html\": \"\\u003Cdiv class=\\\"md\\\"\\u003E\\u003Cp\\u003EYes it does. That fix is also in the master branch, we just haven\\u0026#39;t made a release for it (and we probably won\\u0026#39;t until 3.0).\\u003C/p\\u003E\\n\\u003C/div\\u003E\", \"stickied\": false, \"subreddit\": \"redditdev\", \"score_hidden\": false, \"name\": \"t1_cklhv0f\", \"created\": 1411038834.0, \"author_flair_text\": null, \"created_utc\": 1411010034.0, \"ups\": 1, \"mod_reports\": [], \"num_reports\": null, \"distinguished\": null}}], \"after\": null, \"before\": null}}"
-        },
-        "headers": {
-          "CF-RAY": "286fbfde863239e8-PHX",
-          "Connection": "keep-alive",
-          "Content-Type": "application/json; charset=UTF-8",
-          "Date": "Mon, 21 Mar 2016 07:39:20 GMT",
-          "Server": "cloudflare-nginx",
-          "Strict-Transport-Security": "max-age=15552000; includeSubDomains; preload",
-          "Transfer-Encoding": "chunked",
-          "Vary": "accept-encoding",
-          "X-Moose": "majestic",
-          "access-control-allow-origin": "*",
-          "access-control-expose-headers": "X-Reddit-Tracking, X-Moose",
-          "cache-control": "max-age=0, must-revalidate",
-          "x-content-type-options": "nosniff",
-          "x-frame-options": "SAMEORIGIN",
-          "x-reddit-tracking": "https://pixel.redditmedia.com/pixel/of_destiny.png?v=WfsXsAXtlk2tH%2FAo1Yf9VaNvVLXWyB%2FIdAxER%2B78RuZL0hnQ%2F56eiUdhnUAh9oEZnc18WLb%2Bnks%3D",
-          "x-ua-compatible": "IE=edge",
-          "x-xss-protection": "1; mode=block"
-        },
-        "status": {
-          "code": 200,
-          "message": "OK"
-        },
-        "url": "https://oauth.reddit.com/api/info/?id=t1_cklhv0f&raw_json=1"
-      }
-    },
-    {
-      "recorded_at": "2016-03-21T07:39:21",
-      "request": {
-        "body": {
-          "encoding": "utf-8",
-          "string": ""
-        },
-        "headers": {
-          "Accept": "*/*",
-          "Accept-Encoding": "identity",
-          "Authorization": "bearer -XSZVbwSv9MstucyvALj72ltEJtc",
-          "Connection": "keep-alive",
-          "Cookie": "loid=3Rw5QbNUcPvfBaF0lr; __cfduid=db704537c2aa1a6a32f089173fc65db5c1458545960; loidcreated=2016-03-21T07%3A39%3A20.431Z",
-          "User-Agent": "<USER_AGENT> PRAW/4.0.0a1 prawcore/0.0.7"
-        },
-        "method": "GET",
-        "uri": "https://oauth.reddit.com/comments/2gmzqe/?raw_json=1&limit=2048&sort=best"
-      },
-      "response": {
-        "body": {
-          "encoding": "UTF-8",
-          "string": "[{\"kind\": \"Listing\", \"data\": {\"modhash\": \"\", \"children\": [{\"kind\": \"t3\", \"data\": {\"domain\": \"self.redditdev\", \"banned_by\": null, \"media_embed\": {}, \"subreddit\": \"redditdev\", \"selftext_html\": \"\\u003C!-- SC_OFF --\\u003E\\u003Cdiv class=\\\"md\\\"\\u003E\\u003Cp\\u003EPRAW client developers,\\u003C/p\\u003E\\n\\n\\u003Cp\\u003EI have made a PRAW branch to test using only HTTPS over the API. This change requires some testers to see if there any issues that did not come up from our set of unit tests. This is the first of a few improvements that will (hopefully soon) be released with PRAW version 3. If you want to start using HTTPS exclusively through PRAW please update via the following:\\u003C/p\\u003E\\n\\n\\u003Cpre\\u003E\\u003Ccode\\u003Epip install git+git://github.com/praw-dev/praw.git@praw3\\n\\u003C/code\\u003E\\u003C/pre\\u003E\\n\\n\\u003Cp\\u003EIf you experience any issues feel free to report them here, however filing a bug on github (\\u003Ca href=\\\"https://github.com/praw-dev/praw/issues\\\"\\u003Ehttps://github.com/praw-dev/praw/issues\\u003C/a\\u003E) would be ideal. Thanks!\\u003C/p\\u003E\\n\\u003C/div\\u003E\\u003C!-- SC_ON --\\u003E\", \"selftext\": \"PRAW client developers,\\n\\nI have made a PRAW branch to test using only HTTPS over the API. This change requires some testers to see if there any issues that did not come up from our set of unit tests. This is the first of a few improvements that will (hopefully soon) be released with PRAW version 3. If you want to start using HTTPS exclusively through PRAW please update via the following:\\n\\n    pip install git+git://github.com/praw-dev/praw.git@praw3\\n\\nIf you experience any issues feel free to report them here, however filing a bug on github (https://github.com/praw-dev/praw/issues) would be ideal. Thanks!\", \"likes\": null, \"suggested_sort\": null, \"user_reports\": [], \"secure_media\": null, \"link_flair_text\": null, \"id\": \"2gmzqe\", \"from_kind\": null, \"gilded\": 1, \"archived\": true, \"clicked\": false, \"report_reasons\": null, \"author\": \"bboe\", \"media\": null, \"name\": \"t3_2gmzqe\", \"score\": 12, \"approved_by\": null, \"over_18\": false, \"hidden\": false, \"thumbnail\": \"\", \"subreddit_id\": \"t5_2qizd\", \"edited\": false, \"link_flair_css_class\": null, \"author_flair_css_class\": null, \"downs\": 0, \"mod_reports\": [], \"secure_media_embed\": {}, \"saved\": false, \"removal_reason\": null, \"stickied\": false, \"from\": null, \"is_self\": true, \"from_id\": null, \"permalink\": \"/r/redditdev/comments/2gmzqe/praw_https_enabled_praw_testing_needed/\", \"locked\": false, \"hide_score\": false, \"created\": 1410964471.0, \"url\": \"https://www.reddit.com/r/redditdev/comments/2gmzqe/praw_https_enabled_praw_testing_needed/\", \"author_flair_text\": null, \"quarantine\": false, \"title\": \"[PRAW] HTTPS enabled PRAW testing needed\", \"created_utc\": 1410935671.0, \"ups\": 12, \"upvote_ratio\": 0.89, \"num_comments\": 2, \"visited\": false, \"num_reports\": null, \"distinguished\": null}}], \"after\": null, \"before\": null}}, {\"kind\": \"Listing\", \"data\": {\"modhash\": \"\", \"children\": [{\"kind\": \"t1\", \"data\": {\"subreddit_id\": \"t5_2qizd\", \"banned_by\": null, \"removal_reason\": null, \"link_id\": \"t3_2gmzqe\", \"likes\": null, \"replies\": {\"kind\": \"Listing\", \"data\": {\"modhash\": \"\", \"children\": [{\"kind\": \"t1\", \"data\": {\"subreddit_id\": \"t5_2qizd\", \"banned_by\": null, \"removal_reason\": null, \"link_id\": \"t3_2gmzqe\", \"likes\": null, \"replies\": \"\", \"user_reports\": [], \"saved\": false, \"id\": \"cklhv0f\", \"gilded\": 0, \"archived\": true, \"report_reasons\": null, \"author\": \"bboe\", \"parent_id\": \"t1_cklfmye\", \"score\": 1, \"approved_by\": null, \"controversiality\": 0, \"body\": \"Yes it does. That fix is also in the master branch, we just haven't made a release for it (and we probably won't until 3.0).\", \"edited\": false, \"author_flair_css_class\": null, \"downs\": 0, \"body_html\": \"\\u003Cdiv class=\\\"md\\\"\\u003E\\u003Cp\\u003EYes it does. That fix is also in the master branch, we just haven\\u0026#39;t made a release for it (and we probably won\\u0026#39;t until 3.0).\\u003C/p\\u003E\\n\\u003C/div\\u003E\", \"subreddit\": \"redditdev\", \"name\": \"t1_cklhv0f\", \"score_hidden\": false, \"stickied\": false, \"created\": 1411038834.0, \"author_flair_text\": null, \"created_utc\": 1411010034.0, \"distinguished\": null, \"mod_reports\": [], \"num_reports\": null, \"ups\": 1}}], \"after\": null, \"before\": null}}, \"user_reports\": [], \"saved\": false, \"id\": \"cklfmye\", \"gilded\": 0, \"archived\": true, \"report_reasons\": null, \"author\": \"paneer_burrito\", \"parent_id\": \"t3_2gmzqe\", \"score\": 1, \"approved_by\": null, \"controversiality\": 0, \"body\": \"Quick question: I remember reading about an issue where the proxy settings for https requests were not getting picked up from the environment variables. Does this branch have that fix? Thanks\", \"edited\": false, \"author_flair_css_class\": null, \"downs\": 0, \"body_html\": \"\\u003Cdiv class=\\\"md\\\"\\u003E\\u003Cp\\u003EQuick question: I remember reading about an issue where the proxy settings for https requests were not getting picked up from the environment variables. Does this branch have that fix? Thanks\\u003C/p\\u003E\\n\\u003C/div\\u003E\", \"subreddit\": \"redditdev\", \"name\": \"t1_cklfmye\", \"score_hidden\": false, \"stickied\": false, \"created\": 1411033912.0, \"author_flair_text\": null, \"created_utc\": 1411005112.0, \"distinguished\": null, \"mod_reports\": [], \"num_reports\": null, \"ups\": 1}}], \"after\": null, \"before\": null}}]"
-        },
-        "headers": {
-          "CF-RAY": "286fbfdf463a39e8-PHX",
-          "Connection": "keep-alive",
-          "Content-Type": "application/json; charset=UTF-8",
-          "Date": "Mon, 21 Mar 2016 07:39:20 GMT",
-          "Server": "cloudflare-nginx",
-          "Strict-Transport-Security": "max-age=15552000; includeSubDomains; preload",
-          "Transfer-Encoding": "chunked",
-          "Vary": "accept-encoding",
-          "X-Moose": "majestic",
-          "access-control-allow-origin": "*",
-          "access-control-expose-headers": "X-Reddit-Tracking, X-Moose",
-          "cache-control": "max-age=0, must-revalidate",
-          "x-content-type-options": "nosniff",
-          "x-frame-options": "SAMEORIGIN",
-          "x-reddit-tracking": "https://pixel.redditmedia.com/pixel/of_destiny.png?v=h1RVaF79i29N%2FLPV2JqgxpEqnTiCOlSbDmRWBCaMTdMGeI6kapVqhnFbNm4T1TnbwGt8veMpUH4lE2W0lqqdQREI4DnyDxPB",
-          "x-ua-compatible": "IE=edge",
-          "x-xss-protection": "1; mode=block"
-        },
-        "status": {
-          "code": 200,
-          "message": "OK"
-        },
-        "url": "https://oauth.reddit.com/comments/2gmzqe/?raw_json=1&limit=2048&sort=best"
-      }
-    }
-  ],
-  "recorded_with": "betamax/0.5.1"
-}
\ No newline at end of file
diff --git a/tests/integration/models/reddit/test_comment.py b/tests/integration/models/reddit/test_comment.py
--- a/tests/integration/models/reddit/test_comment.py
+++ b/tests/integration/models/reddit/test_comment.py
@@ -14,7 +14,6 @@ def test_attributes(self):
             assert comment.author == 'bboe'
             assert comment.body.startswith('Yes it does.')
             assert not comment.is_root
-            assert comment.permalink(fast=True) == '/comments/2gmzqe//cklhv0f'
             assert comment.submission == '2gmzqe'
 
     @mock.patch('time.sleep', return_value=None)
@@ -150,13 +149,6 @@ def test_parent__submission(self):
         assert isinstance(parent, Submission)
         assert parent.fullname == comment.parent_id
 
-    def test_permalink(self):
-        with self.recorder.use_cassette('TestComment.test_permalink'):
-            comment = Comment(self.reddit, 'cklhv0f')
-            assert comment.permalink() == ('/r/redditdev/comments/2gmzqe/'
-                                           'praw_https_enabled_praw_testing_'
-                                           'needed/cklhv0f')
-
     def test_refresh(self):
         with self.recorder.use_cassette('TestComment.test_refresh'):
             comment = Comment(self.reddit, 'd81vwef').refresh()
diff --git a/tests/integration/models/reddit/test_message.py b/tests/integration/models/reddit/test_message.py
--- a/tests/integration/models/reddit/test_message.py
+++ b/tests/integration/models/reddit/test_message.py
@@ -23,7 +23,7 @@ def test_attributes(self, _):
                     assert isinstance(message.replies, list)
                     assert message.subreddit is None \
                         or isinstance(message.subreddit, Subreddit)
-                except:
+                except Exception:
                     import pprint
                     pprint.pprint(vars(message))
                     raise