Repository: frictionlessdata/frictionless-py

Issue:
Have sensible defaults for `innerpath` in `Package` class
# Overview

Current behaviour when instantiating the `Package` class with a zip file or url is to look for a file named `datapackage.json` inside. If none is found, it throws an exception.

## Example

```python
package = Package('http://repositorio.dados.gov.br/seges/consumo-energia/consumo-energia.zip')
```

this throws an exception because the zip file does not contain a `datapackage.json`, but a `datapackage.yaml`. Specifying an `innerpath` argument makes it work:

```python
package = Package('http://repositorio.dados.gov.br/seges/consumo-energia/consumo-energia.zip', innerpath='datapackage.yaml')
```

## Proposal

In order to make the Frictionless Framework more usable and simplify user's code it should have sensible defaults. It could look first for a `datapackage.json`, then look for a `datapackage.yaml` (maybe also `.yml`), and only if none is found raise an exception.

Discussion:
kagawa0710: @augusto-herrmann 
Thanks for the suggestion.I think it's better this way too!
Commit accepted.


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e28aa022..52c77e6c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -59,7 +59,14 @@ Frictionless is a Python3.6+ framework, and it uses some common Python tools for
 - formatting: `black`
 - type checking: `mypy` (under construction)
 
-You also need `git` to work on the project, and `make` is recommended. After cloning the repository, we recommend creating a virtual environment and installing the dependencies by following this code:
+You also need `git` to work on the project, and `make` is recommended.
+
+After cloning the repository, you can set up the development environment
+either by creating a virtual environment or a docker container.
+
+### Using a virtual environment
+
+Create a virtual environment and install the dependencies by following this code:
 
 > this will install a `git commit` hook running the tests
 
@@ -73,6 +80,32 @@ alias "frictionless=python -m frictionless"
 
 Note: You may need to run `sudo apt-get install postgresql libpq-dev` on a Debian-based system, because the python Postgres module depends on some postgres CLI tools.
 
+### Using a Docker container
+
+Use the following command to build the container:
+
+```bash
+make docker-setup
+```
+
+This should take care of setting up everything. If the container is
+built without errors, you can then run commands like `make` inside the
+container to accomplish various tasks (see the next section for details).
+
+To make things easier, we can create an alias:
+
+```bash
+alias "frictionless-dev=docker run --rm -v $PWD:/home/frictionless -it frictionless-dev"
+```
+
+Then, for example, to run the tests, we can use:
+
+```bash
+frictionless-dev make test
+```
+
+### Using make
+
 Then you can run various make commands:
 - `make docs` - build the docs
 - `make format` - format source code
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..6ec0e503
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,19 @@
+# DESCRIPTION: Frictionless Development Environment
+# BUILD: docker build --rm -t frictionless-dev .
+#
+# For running tests using the container, do:
+#   docker run --rm -v $PWD:/home/frictionless -it frictionless-dev make test
+FROM ubuntu:22.04
+
+RUN apt-get update \
+    && apt-get install -y --no-install-recommends \
+        make \
+        python3-pip \
+        python3-psycopg2
+
+COPY . /home/frictionless
+
+WORKDIR /home/frictionless
+
+RUN pip install wheel \
+    && make install
diff --git a/Makefile b/Makefile
index 5d5b216b..3f488d65 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: all coverage docs install format github lint release test test-ci
+.PHONY: all coverage docker-setup docs install format github lint release test test-ci
 
 
 PACKAGE := $(shell grep '^PACKAGE =' setup.py | cut -d '"' -f2)
@@ -12,6 +12,9 @@ all:
 coverage:
 	sensible-browser coverage/index.html
 
+docker-setup:
+	docker build --rm -t frictionless-dev .
+
 docs:
 	python docs/build.py
 
diff --git a/frictionless/assets/VERSION b/frictionless/assets/VERSION
index c62374aa..70d082eb 100644
--- a/frictionless/assets/VERSION
+++ b/frictionless/assets/VERSION
@@ -1,1 +1,1 @@
-4.40.3
+4.40.4
diff --git a/frictionless/helpers.py b/frictionless/helpers.py
index 8686b11e..345d5edf 100644
--- a/frictionless/helpers.py
+++ b/frictionless/helpers.py
@@ -305,6 +305,14 @@ def unzip_descriptor(path, innerpath):
             tempdir = tempfile.mkdtemp()
             zip.extractall(tempdir)
             atexit.register(shutil.rmtree, tempdir)
+            if innerpath == "":
+                innerpath = "datapackage.json"
+                extensions = ("json", "yaml", "yml")
+                default_names = (f"datapackage.{ext}" for ext in extensions)
+                for name in default_names:
+                    if os.path.isfile(os.path.join(tempdir, name)):
+                        innerpath = name
+                        break
             descriptor = os.path.join(tempdir, innerpath)
     return descriptor
 
diff --git a/frictionless/package/package.py b/frictionless/package/package.py
index 5cc1ca9e..2ac5134a 100644
--- a/frictionless/package/package.py
+++ b/frictionless/package/package.py
@@ -99,7 +99,7 @@ class Package(Metadata):
         innerpath? (str): A ZIP datapackage descriptor inner path.
             Path to the package descriptor inside the ZIP datapackage.
             Example: some/folder/datapackage.yaml
-            Default: datapackage.json
+            Default: datapackage.json, datapackage.yaml or datapackage.yml
 
         basepath? (str): A basepath of the resource
             The fullpath of the resource is joined `basepath` and /path`
@@ -154,7 +154,7 @@ class Package(Metadata):
         image=None,
         created=None,
         # Extra
-        innerpath="datapackage.json",
+        innerpath="",
         basepath="",
         detector=None,
         onerror="ignore",
diff --git a/frictionless/plugins/csv/dialect.py b/frictionless/plugins/csv/dialect.py
index 3993b399..2d288290 100644
--- a/frictionless/plugins/csv/dialect.py
+++ b/frictionless/plugins/csv/dialect.py
@@ -141,7 +141,6 @@ class CsvDialect(Dialect):
 
     metadata_profile = {  # type: ignore
         "type": "object",
-        "additionalProperties": False,
         "properties": {
             "delimiter": {"type": "string"},
             "lineTerminator": {"type": "string"},


Test patch:
diff --git a/tests/package/test_general.py b/tests/package/test_general.py
index fbb7114e..f5954a23 100644
--- a/tests/package/test_general.py
+++ b/tests/package/test_general.py
@@ -186,6 +186,13 @@ def test_package_from_zip_innerpath():
     assert len(package.resources) == 10
 
 
+def test_package_from_zip_innerpath_yaml():
+    # for issue1174
+    package = Package("data/innerpath.package.zip")
+    assert package.name == "emissions"
+    assert len(package.resources) == 10
+
+
 @pytest.mark.parametrize("create_descriptor", [(False,), (True,)])
 def test_package_standard_specs_properties(create_descriptor):
     options = dict(
diff --git a/tests/resource/test_dialect.py b/tests/resource/test_dialect.py
index 0e8fac06..2c7c63d5 100644
--- a/tests/resource/test_dialect.py
+++ b/tests/resource/test_dialect.py
@@ -109,6 +109,7 @@ def test_resource_dialect_json_property():
         ]
 
 
+@pytest.mark.skip
 def test_resource_dialect_bad_property():
     resource = Resource("data/table.csv", dialect={"bad": True})
     with pytest.raises(FrictionlessException) as excinfo: