Repository: conan-io/conan
Pull request #3702 (2018-10-09 20:55:53)

#3671 Add attribute tags
Changelog: Feature

This PR is related to #3671 

Also, I added `author` as default attribute when running `conan new`

Docs PR: https://github.com/conan-io/docs/pull/875

- [X] Refer to the issue that supports this Pull Request.
- [X] If the issue has missing info, explain the purpose/use case/pain/need that covers this Pull Request.
- [X] I've read the [Contributing guide](https://raw.githubusercontent.com/conan-io/conan/develop/.github/CONTRIBUTING.md).
- [X] I've followed the PEP8 style guides for Python code.
- [X] I've opened another PR in the Conan docs repo to the ``develop`` branch, documenting this one. 





Patch:
diff --git a/conans/client/cmd/new.py b/conans/client/cmd/new.py
index 29e643a28..17e7e4766 100644
--- a/conans/client/cmd/new.py
+++ b/conans/client/cmd/new.py
@@ -11,8 +11,10 @@ class {package_name}Conan(ConanFile):
     name = "{name}"
     version = "{version}"
     license = "<Put the package license here>"
+    author = "<Put your name here> <And your email here>"
     url = "<Package recipe repository url here, for issues about the package>"
     description = "<Description of {package_name} here>"
+    topics = ("<Put some tag here>", "<here>", "<and here>")
     settings = "os", "compiler", "build_type", "arch"
     options = {{"shared": [True, False]}}
     default_options = "shared=False"
@@ -62,6 +64,8 @@ class {package_name}Conan(ConanFile):
     description = "<Description of {package_name} here>"
     url = "None"
     license = "None"
+    author = "None"
+    topics = None
 
     def package(self):
         self.copy("*")
@@ -77,8 +81,10 @@ class {package_name}Conan(ConanFile):
     name = "{name}"
     version = "{version}"
     license = "<Put the package license here>"
+    author = "<Put your name here> <And your email here>"
     url = "<Package recipe repository url here, for issues about the package>"
     description = "<Description of {package_name} here>"
+    topics = ("<Put some tag here>", "<here>", "<and here>")
     settings = "os", "compiler", "build_type", "arch"
     options = {{"shared": [True, False]}}
     default_options = "shared=False"
@@ -116,8 +122,10 @@ class {package_name}Conan(ConanFile):
     name = "{name}"
     version = "{version}"
     license = "<Put the package license here>"
+    author = "<Put your name here> <And your email here>"
     url = "<Package recipe repository url here, for issues about the package>"
     description = "<Description of {package_name} here>"
+    topics = ("<Put some tag here>", "<here>", "<and here>")
     no_copy_source = True
     # No settings/options are necessary, this is header only
 
diff --git a/conans/client/conan_api.py b/conans/client/conan_api.py
index aadb27449..db42879ea 100644
--- a/conans/client/conan_api.py
+++ b/conans/client/conan_api.py
@@ -294,9 +294,9 @@ class ConanAPIV1(object):
 
         result = OrderedDict()
         if not attributes:
-            attributes = ['name', 'version', 'url', 'license', 'author', 'description',
-                          'generators', 'exports', 'exports_sources', 'short_paths',
-                          'apply_env',  'build_policy']
+            attributes = ['name', 'version', 'url', 'homepage', 'license', 'author',
+                          'description', 'topics', 'generators', 'exports', 'exports_sources',
+                          'short_paths', 'apply_env', 'build_policy']
         for attribute in attributes:
             try:
                 attr = getattr(conanfile, attribute)
diff --git a/conans/client/graph/grapher.py b/conans/client/graph/grapher.py
index 157e76bb5..de0fb876c 100644
--- a/conans/client/graph/grapher.py
+++ b/conans/client/graph/grapher.py
@@ -143,8 +143,10 @@ class ConanHTMLGrapher(object):
                 for name, data in [("id", conanfile.info.package_id()),
                                    ("build_id", build_id(conanfile)),
                                    ("url", '<a href="{url}">{url}</a>'.format(url=conanfile.url)),
+                                   ("homepage", '<a href="{url}">{url}</a>'.format(url=conanfile.homepage)),
                                    ("license", conanfile.license),
-                                   ("author", conanfile.author)]:
+                                   ("author", conanfile.author),
+                                   ("topics", conanfile.topics)]:
                     if data:
                         data = data.replace("'", '"')
                         fulllabel.append("<li><b>%s</b>: %s</li>" % (name, data))
diff --git a/conans/client/printer.py b/conans/client/printer.py
index 3aaf3931c..f8f95b46d 100644
--- a/conans/client/printer.py
+++ b/conans/client/printer.py
@@ -111,10 +111,14 @@ class Printer(object):
                 else:
                     self._out.writeln("    Remote: None", Color.BRIGHT_GREEN)
             url = getattr(conan, "url", None)
+            homepage = getattr(conan, "homepage", None)
             license_ = getattr(conan, "license", None)
             author = getattr(conan, "author", None)
+            topics = getattr(conan, "topics", None)
             if url and show("url"):
                 self._out.writeln("    URL: %s" % url, Color.BRIGHT_GREEN)
+            if homepage and show("homepage"):
+                self._out.writeln("    Homepage: %s" % homepage, Color.BRIGHT_GREEN)
 
             if license_ and show("license"):
                 if isinstance(license_, (list, tuple, set)):
@@ -123,6 +127,11 @@ class Printer(object):
                     self._out.writeln("    License: %s" % license_, Color.BRIGHT_GREEN)
             if author and show("author"):
                 self._out.writeln("    Author: %s" % author, Color.BRIGHT_GREEN)
+            if topics and show("topics"):
+                if isinstance(topics, (list, tuple, set)):
+                    self._out.writeln("    Topics: %s" % ", ".join(topics), Color.BRIGHT_GREEN)
+                else:
+                    self._out.writeln("    Topics: %s" % topics, Color.BRIGHT_GREEN)
 
             if isinstance(ref, ConanFileReference) and show("recipe"):  # Excludes PROJECT
                 self._out.writeln("    Recipe: %s" % node.recipe)
diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py
index 3625a3695..105121216 100644
--- a/conans/model/conan_file.py
+++ b/conans/model/conan_file.py
@@ -90,6 +90,8 @@ class ConanFile(object):
     license = None
     author = None  # Main maintainer/responsible for the package, any format
     description = None
+    topics = None
+    homepage = None
     build_policy = None
     short_paths = False
     apply_env = True  # Apply environment variables from requires deps_env_info and profiles


Test patch:
diff --git a/conans/test/command/info_test.py b/conans/test/command/info_test.py
index c3aea03d7..ea024fa46 100644
--- a/conans/test/command/info_test.py
+++ b/conans/test/command/info_test.py
@@ -473,3 +473,58 @@ class AConan(ConanFile):
 
         self.client.run("info conanfile.txt", ignore_error=True)
         self.assertIn("ERROR: Conanfile not found", self.client.out)
+
+    def test_common_attributes(self):
+        self.client = TestClient()
+
+        conanfile = """from conans import ConanFile
+from conans.util.files import load, save
+
+class MyTest(ConanFile):
+    name = "Pkg"
+    version = "0.1"
+    settings = "build_type"
+
+"""
+
+        self.client.save({"subfolder/conanfile.py": conanfile})
+        self.client.run("export ./subfolder lasote/testing")
+
+        self.client.run("info ./subfolder")
+
+        self.assertIn("Pkg/0.1@PROJECT", self.client.user_io.out)
+        self.assertNotIn("License:", self.client.user_io.out)
+        self.assertNotIn("Author:", self.client.user_io.out)
+        self.assertNotIn("Topics:", self.client.user_io.out)
+        self.assertNotIn("Homepage:", self.client.user_io.out)
+        self.assertNotIn("URL:", self.client.user_io.out)
+
+    def test_full_attributes(self):
+        self.client = TestClient()
+
+        conanfile = """from conans import ConanFile
+from conans.util.files import load, save
+
+class MyTest(ConanFile):
+    name = "Pkg"
+    version = "0.2"
+    settings = "build_type"
+    author = "John Doe"
+    license = "MIT"
+    url = "https://foo.bar.baz"
+    homepage = "https://foo.bar.site"
+    topics = ["foo", "bar", "qux"]
+
+"""
+
+        self.client.save({"subfolder/conanfile.py": conanfile})
+        self.client.run("export ./subfolder lasote/testing")
+
+        self.client.run("info ./subfolder")
+
+        self.assertIn("Pkg/0.2@PROJECT", self.client.user_io.out)
+        self.assertIn("License: MIT", self.client.user_io.out)
+        self.assertIn("Author: John Doe", self.client.user_io.out)
+        self.assertIn("Topics: foo, bar, qux", self.client.user_io.out)
+        self.assertIn("URL: https://foo.bar.baz", self.client.user_io.out)
+        self.assertIn("Homepage: https://foo.bar.site", self.client.user_io.out)
diff --git a/conans/test/command/inspect_test.py b/conans/test/command/inspect_test.py
index 1b3f529fb..0119dc7a2 100644
--- a/conans/test/command/inspect_test.py
+++ b/conans/test/command/inspect_test.py
@@ -134,12 +134,49 @@ class Pkg(ConanFile):
         self.assertIn("""name: MyPkg
 version: 1.2.3
 url: None
+homepage: None
 license: None
 author: None
 description: None
+topics: None
 generators: ['txt']
 exports: None
 exports_sources: None
 short_paths: False
 apply_env: True
+build_policy: None
+""", client.out)
+
+    def test_inspect_filled_attributes(self):
+        client = TestClient()
+        conanfile = """from conans import ConanFile
+class Pkg(ConanFile):
+    name = "MyPkg"
+    version = "1.2.3"
+    author = "John Doe"
+    url = "https://john.doe.com"
+    homepage = "https://john.company.site"
+    license = "MIT"
+    description = "Yet Another Test"
+    generators = "cmake"
+    topics = ["foo", "bar", "qux"]
+    _private = "Nothing"
+    def build(self):
+        pass
+"""
+        client.save({"conanfile.py": conanfile})
+        client.run("inspect .")
+        self.assertIn("""name: MyPkg
+version: 1.2.3
+url: https://john.doe.com
+homepage: https://john.company.site
+license: MIT
+author: John Doe
+description: Yet Another Test
+topics: ['foo', 'bar', 'qux']
+generators: cmake
+exports: None
+exports_sources: None
+short_paths: False
+apply_env: True
 build_policy: None""", client.out)
diff --git a/conans/test/command/new_test.py b/conans/test/command/new_test.py
index d79601d19..c6b035077 100644
--- a/conans/test/command/new_test.py
+++ b/conans/test/command/new_test.py
@@ -61,6 +61,8 @@ class NewTest(unittest.TestCase):
         content = load(os.path.join(root, "conanfile.py"))
         self.assertIn('name = "MyPackage"', content)
         self.assertIn('version = "1.3"', content)
+        self.assertIn('topics = (', content)
+        self.assertNotIn('homepage', content)
         self.assertTrue(os.path.exists(os.path.join(root, "test_package/conanfile.py")))
         self.assertTrue(os.path.exists(os.path.join(root, "test_package/CMakeLists.txt")))
         self.assertTrue(os.path.exists(os.path.join(root, "test_package/example.cpp")))
@@ -78,6 +80,8 @@ class NewTest(unittest.TestCase):
         self.assertIn('name = "MyPackage"', content)
         self.assertIn('version = "1.3"', content)
         self.assertIn('exports_sources', content)
+        self.assertIn('topics = (', content)
+        self.assertNotIn('homepage', content)
         self.assertNotIn('source()', content)
         # assert they are correct at least
         client.run("export . myuser/testing")
@@ -93,6 +97,8 @@ class NewTest(unittest.TestCase):
         self.assertIn('name = "MyPackage"', content)
         self.assertIn('version = "1.3"', content)
         self.assertIn('del self.settings.compiler.libcxx', content)
+        self.assertIn('topics = (', content)
+        self.assertNotIn('homepage', content)
         # assert they are correct at least
         client.run("export . myuser/testing")
         client.run("search")