← run

bcb-0014

1.000
4/4 tests· lib-knowledge
Challenge · difficulty 3/5
# BigCodeBench/14

Implement a file **`solution.py`** that completes the function below. Keep the given name and signature; define `task_func` at module level.

Allowed libraries: `configparser`, `shutil`, `os`.

```python
import configparser
import os
import shutil


def task_func(config_file_path, archieve_dir ='/home/user/archive'):
    """
    Archive a specified project directory into a ZIP file based on the configuration specified in a config file.
    
    This function reads a configuration file to determine the project directory and archives this directory into a ZIP file.
    The ZIP file's name will be the project directory's basename, stored in the specified archive directory.
    
    Configuration File Format:
    [Project]
    directory=path_to_project_directory
    
    Parameters:
    - config_file_path (str): Path to the configuration file. The file must exist and be readable.
    - archive_dir (str, optional): Path to the directory where the ZIP archive will be stored. Defaults to '/home/user/archive'.
    
    Returns:
    - bool: True if the ZIP archive is successfully created, otherwise an exception is raised.
    
    Requirements:
    - configparse
    - os
    - shutil

    Raises:
    - FileNotFoundError: If the `config_file_path` does not exist or the specified project directory does not exist.
    - Exception: If the ZIP archive cannot be created.
    
    Example:
    >>> task_func("/path/to/config.ini")
    True
    """
```

<!-- imported from BigCodeBench (BigCodeBench/14) -->
tests/test_bcb_0014.py
# Auto-generated from BigCodeBench BigCodeBench/14. Do not edit by hand.
import pathlib as _pathlib
exec(_pathlib.Path(__file__).with_name("solution.py").read_text(), globals())

import unittest
import tempfile
import shutil
import os
import configparser
class TestCases(unittest.TestCase):
    def setUp(self):
        # Setup a temporary directory for the configuration files and another for the archive output
        self.test_data_dir = tempfile.mkdtemp()
        self.archive_dir = tempfile.mkdtemp()
        # Example valid configuration file setup
        self.valid_config_path = os.path.join(self.test_data_dir, "valid_config.ini")
        config = configparser.ConfigParser()
        config['Project'] = {'directory': self.test_data_dir}
        with open(self.valid_config_path, 'w') as configfile:
            config.write(configfile)
        # Invalid directory config
        self.invalid_config_path = os.path.join(self.test_data_dir, "invalid_config.ini")
        config['Project'] = {'directory': '/path/to/nonexistent/directory'}
        with open(self.invalid_config_path, 'w') as configfile:
            config.write(configfile)
    def tearDown(self):
        # Remove temporary directories after each test
        shutil.rmtree(self.test_data_dir)
        shutil.rmtree(self.archive_dir)
    def test_valid_project_directory(self):
        # Testing with a valid project directory
        result = task_func(self.valid_config_path, self.archive_dir)
        self.assertTrue(result)
    def test_invalid_project_directory(self):
        # Testing with a non-existent project directory
        with self.assertRaises(FileNotFoundError):
            task_func(self.invalid_config_path, self.archive_dir)
    def test_archive_creation(self):
        # Run the function to create the archive
        task_func(self.valid_config_path, self.archive_dir)
        archive_file = os.path.join(self.archive_dir, os.path.basename(self.test_data_dir) + '.zip')
        self.assertTrue(os.path.isfile(archive_file))
    def test_archive_content(self):
        # Adding a sample file to the project directory to check archive contents later
        sample_file_path = os.path.join(self.test_data_dir, "sample_file.txt")
        with open(sample_file_path, 'w') as f:
            f.write("Hello, world!")
        task_func(self.valid_config_path, self.archive_dir)
        archive_file = os.path.join(self.archive_dir, os.path.basename(self.test_data_dir) + '.zip')
        content = os.popen(f"unzip -l {archive_file}").read()
        self.assertIn("sample_file.txt", content)
System prompt
You are an expert programmer. Solve the task exactly as specified. Output your solution as fenced code blocks using the required file name(s) and the exact function/type signatures requested. Prefer correctness; do not include prose outside code unless asked.
Proposed solution
```python
import configparser
import os
import shutil


def task_func(config_file_path, archieve_dir='/home/user/archive'):
    if not os.path.exists(config_file_path):
        raise FileNotFoundError(f"Config file not found: {config_file_path}")
        
    config = configparser.ConfigParser()
    config.read(config_file_path)
    
    project_dir = os.path.normpath(config['Project']['directory'].strip())
    
    if not os.path.exists(project_dir):
        raise FileNotFoundError(f"Project directory not found: {project_dir}")
        
    os.makedirs(archieve_dir, exist_ok=True)
    
    archive_path = os.path.join(archieve_dir, os.path.basename(project_dir))
    shutil.make_archive(archive_path, 'zip', project_dir)
    
    return True
```

Test output (stdout)

....                                                                     [100%]
4 passed in 0.02s
Reasoning (chain-of-thought)
The user wants me to implement a function `task_func` that:
1. Reads a configuration file using `configparser`
2. Extracts the project directory path from the `[Project]` section under the `directory` key
3. Archives that directory into a ZIP file
4. The ZIP file name should be the basename of the project directory
5. The ZIP file should be stored in `archive_dir` (default `/home/user/archive`)
6. Returns `True` if successful
7. Raises `FileNotFoundError` if config file or project directory doesn't exist
8. Raises `Exception` if ZIP creation fails

Allowed libraries: `configparser`, `shutil`, `os`

Wait, `shutil` has `make_archive` which can create ZIP files. Let me check the signature:
`shutil.make_archive(base_name, format, root_dir=None, base_dir=None, ...)`

Actually, `shutil.make_archive` creates an archive. The `base_name` is the full path of the archive file (without extension). The `format` would be 'zip'.

Let's think about the implementation:
1. Check if config file exists, raise FileNotFoundError if not
2. Read config file with configparser
3. Get directory path from `[Project]` section, `directory` key
4. Check if directory exists, raise FileNotFoundError if not
5. Create archive_dir if it doesn't exist (using os.makedirs)
6. Create ZIP archive using shutil.make_archive
7. Return True

Let's refine:
```python
import configparser
import os
import shutil

def task_func(config_file_path, archieve_dir='/home/user/archive'):
    if not os.path.exists(config_file_path):
        raise FileNotFoundError(f"Config file not found: {config_file_path}")
        
    config = configparser.ConfigParser()
    config.read(config_file_path)
    
    project_dir = config['Project']['directory']
    
    if not os.path.exists(project_dir):
        raise FileNotFoundError(f"Project directory not found: {project_dir}")
        
    os.makedirs(archieve_dir, exist_ok=True)
    
    base_name = os.path.join(archieve_dir, os.path.basename(project_dir))
    shutil.make_archive(base_name, 'zip', root_dir=os.path.dirname(project_dir), base_dir=os.path.basename(project_dir))
    
    return True
```

Wait, `shutil.make_archive` signature:
`shutil.make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=False, owner=None, group=None, logger=None)`

If `root_dir` is the parent of `project_dir` and `base_dir` is the basename of `project_dir`, it will archive the contents of `project_dir` into the ZIP.

Actually, a simpler approach:
`shutil.make_archive(base_name, 'zip', project_dir)`
This archives the contents of `project_dir`.

Let's verify: `shutil.make_archive('/path/to/archive/myproject', 'zip', '/path/to/myproject')` creates `/path/to/archive/myproject.zip` containing the contents of `/path/to/myproject`.

Wait, the problem says "The ZIP file's name will be the project directory's basename". So if project_dir is `/home/user/projects/myproject`, the ZIP should be `myproject.zip` in `archive_dir`.

Let's check `shutil.make_archive` behavior:
`shutil.make_archive(base_name, format, root_dir)` archives `root_dir` into `base_name.format`.
So `shutil.make_archive(os.path.join(archieve_dir, os.path.basename(project_dir)), 'zip', project_dir)` should work.

Actually, let's be careful about `root_dir` vs `base_dir`. According to docs:
- `root_dir`: Directory to archive. If not specified, defaults to `base_dir`.
- `base_dir`: Directory to start archiving from.

So `shutil.make_archive(base_name, 'zip', project_dir)` should work fine.

Let's also handle the case where the config file might not have the section/key properly, but the problem says "The file must exist and be readable" and gives the format. I'll assume it's well-formed or let configparser raise its own error, but the problem specifically mentions raising FileNotFoundError for missing files.

One thing: the parameter name in the signature is `archieve_dir` (typo in the prompt: "archieve" instead of "archive"). I should keep it exactly as given.

Let's double check the exact signature:
`def task_func(config_file_path, archieve_dir ='/home/user/archive'):`

I'll keep the typo as is.

Wait, should I check if `config_file_path` exists before reading? Yes.
Should I check if `project_dir` exists? Yes.

Let's write it out carefully:

```python
import configparser
import os
import shutil

def task_func(config_file_path, archieve_dir='/home/user/archive'):
    if not os.path.exists(config_file_path):
        raise FileNotFoundError(f"Config file not found: {config_file_path}")
        
    config = configparser.ConfigParser()
    config.read(config_file_path)
    
    project_dir = config['Project']['directory']
    
    if not os.path.exists(project_dir):
        raise FileNotFoundError(f"Project directory not found: {project_dir}")
        
    os.makedirs(archieve_dir, exist_ok=True)
    
    archive_name = os.path.join(archieve_dir, os.path.basename(project_dir))
    shutil.make_archive(archive_name, 'zip', project_dir)
    
    return True
```

Wait, `shutil.make_archive` might raise an exception if it fails, which matches the requirement "otherwise an exception is raised".

Let's verify `shutil.make_archive` behavior with `root_dir`. If `project_dir` is `/path/to/project`, `shutil.make_archive(archive_name, 'zip', project_dir)` will create `archive_name.zip` containing the contents of `project_dir`. This is correct.

One minor point: `config.read()` doesn't raise an error if the file doesn't exist, it just returns an empty list. That's why I check `os.path.exists` first. Alternatively, I could use `config.read_file()` after opening, but `os.path.exists` is fine.

Actually, `configparser` in Python 3.2+ raises `MissingSectionHeaderError` or similar if the file is malformed, but for existence, `os.path.exists` is correct.

Wait, should I strip whitespace from the directory path? `config['Project']['directory']` might have trailing whitespace. It's safer to `.strip()` it.
`project_dir = config['Project']['directory'].strip()`

Also, the problem says "The ZIP file's name will be the project directory's basename". So `os.path.basename(project_dir)` is correct.

Let's consider 
…[9299 chars elided]…
ompt has `archieve_dir ='/home/user/archive'` with a space. I'll keep standard formatting `archieve_dir='/home/user/archive'` as Python ignores whitespace around `=` in function definitions. It's fine.
All set. 
Output matches. 
Proceeds. 
[Done] 
Output Generation. 
```python
import configparser
import os
import shutil


def task_func(config_file_path, archieve_dir='/home/user/archive'):
    if not os.path.exists(config_file_path):
        raise FileNotFoundError(f"Config file not found: {config_file_path}")
        
    config = configparser.ConfigParser()
    config.read(config_file_path)
    
    project_dir = os.path.normpath(config['Project']['directory'].strip())
    
    if not os.path.exists(project_dir):
        raise FileNotFoundError(f"Project directory not found: {project_dir}")
        
    os.makedirs(archieve_dir, exist_ok=True)
    
    archive_path = os.path.join(archieve_dir, os.path.basename(project_dir))
    shutil.make_archive(archive_path, 'zip', project_dir)
    
    return True
```
Looks correct. I will present it. 
Note: I'll make sure the indentation and formatting match standard Python conventions. The function signature matches exactly. All constraints are met.
Done. 
Output matches response. 
Proceeds. 
[Final Check]
- `configparser`, `shutil`, `os` imported
- `task_func` defined at module level
- Signature matches
- Raises `FileNotFoundError` appropriately
- Returns `True` on success
- Uses `shutil.make_archive` for ZIP creation
- Handles directory creation
- Strips whitespace and normalizes path
All good. 
Output generation. 
(Self-Correction/Refinement during thought)
Wait, `shutil.make_archive` might behave differently if `project_dir` is a relative path. `os.path.normpath` handles it. `os.path.exists` works with relative paths. It should be fine.
The problem statement says "The ZIP file's name will be the project directory's basename". `os.path.basename(project_dir)` gives exactly that.
Everything is consistent.
I will output the solution now. 
[Output Generation] -> *Proceeds*